《数据结构与算法应用》
上机大作业
学院:电子工程学院 专业:遥感科学与技术
班级:1702071
组员:
选题:第一题、第二题、第六题
1 / 35
目录
第一题………………………………………………………………………………P3 第二题………………………………………………………………………………P13 第六题………………………………………………………………………………P22 实验心得体会………………………………………………………………………P34 详细分工及成绩申报………………………………………………………………P35
2 / 35
第一题
一.题目
利用带头结点的单链表实现两个集合的并、交、差运算。(难易程度:低)
二.题目功能描述
1、要求用带头结点的单链表存储两个集合中的元素和最终的结果。
2、集合的元素限定为十进制数,程序应对出现重复的数据进行过滤,即使得链 表中没有重复数据。
3、显示两个集合的内容及其并集、交集和差集的内容。
4、要求不改变原来的集合,并集、交集和差集分别另外存放。
三.概要设计
建立单链表过滤数据求集合的交,并,差集
四.程序源代码及相应的流程图
1.程序源代码
#include datatype data; struct LNode *next; } LinkList; void DispList(LinkList*L) { LinkList *p=L->next; while(p!=NULL) 3 / 35 { printf(\"%d \ p=p->next; } printf(\"\\n\"); } void DestroyList(LinkList *&L) { LinkList*p=L->next,*pre=L; while(p!=NULL) { free(pre); pre=p; p=pre->next; } free(pre); } LinkList*CreateListF()//尾插法建立单链表 { LinkList*head,*p,*r,*s; datatype CreateListF_num; int n; printf(\"创立一个链表,大小为:\"); scanf(\"%d\ head=(LinkList*)malloc(sizeof(LinkList)); head->next=NULL; r=head; printf(\"输入以空格隔开的十进制整数:\"); for(int i=1,k=1;i<=n;i++,k++) { int flag=1; s=head->next; scanf(\"%d\ for(int j=1;j 4 / 35 p=(LinkList*)malloc(sizeof(LinkList)); p->data=CreateListF_num; r->next=p; r=p; } } r->next=NULL; return head; } ///从小到大排序 void sort(LinkList *L) { LinkList *pre,*p,*q; if(L->next!=NULL) { p=L->next->next; L->next->next=NULL; while(p!=NULL) { q=p->next; pre=L; while(pre->next!=NULL&&pre->next->data void Delete(LinkList *head) { if(head->next!=NULL) { LinkList *p=head->next,*r=head,*q; while(p->next) { if(p->data==p->next->data) { q=p->next; p->next=q->next; free(q); } 5 / 35 else p=p->next; } } } ///求集合的并 void Union(LinkList *ha,LinkList*hb,LinkList*&hc) { LinkList *pa=ha->next,*pb=hb->next,*pc,*s; hc=(LinkList*)malloc(sizeof(LinkList)); pc=hc; while(pa!=NULL &&pb!=NULL ) { if(pa->data s=(LinkList*)malloc(sizeof(LinkList)); s->data=pa->data; pc->next=s; pc=s; pa=pa->next; } else if(pa->data>pb->data) { s=(LinkList*)malloc(sizeof(LinkList)); s->data=pb->data; pc->next=s; pc=s; pb=pb->next; } else { s=(LinkList*)malloc(sizeof(LinkList)); s->data=pa->data; pc->next=s; pc=s; pa=pa->next; pb=pb->next; } } if(pb!=NULL) pa=pb; while(pa!=NULL) { 6 / 35 s=(LinkList*)malloc(sizeof(LinkList)); s->data=pa->data; pc->next=s; pc=s; pa=pa->next; } pc->next=NULL; } ///求两个有序集合的交用尾插法 void InterSect(LinkList *ha,LinkList*hb,LinkList*&hc) { LinkList *pa=ha->next,*pb,*pc,*s; hc=(LinkList*)malloc(sizeof(LinkList)); pc=hc; while(pa!=NULL) { pb=hb->next; while(pb!=NULL&&pb->data if(pb!=NULL &&pb->data==pa->data)///B节点在A节点中复制A节点 { s=(LinkList*)malloc(sizeof(LinkList)); s->data=pa->data; pc->next=s; pc=s; } pa=pa->next; } pc->next=NULL; } ///求两个有序集合的差 void Subs(LinkList *ha,LinkList*hb,LinkList*&hc) { LinkList *pa=ha->next,*pb,*pc,*s; hc=(LinkList*)malloc(sizeof(LinkList)); pc=hc; while(pa!=NULL) { pb=hb->next; while(pb!=NULL&&pb->data if(!(pb!=NULL &&pb->data==pa->data))///B节点不在A节点中复制A节点 { s=(LinkList*)malloc(sizeof(LinkList)); 7 / 35 s->data=pa->data; pc->next=s; pc=s; } pa=pa->next; } pc->next=NULL; } int main() { LinkList *ha,*hb,*hc; ha=CreateListF(); hb=CreateListF(); printf(\" 集合的运算如下:\\n\"); printf(\" 原集合A: \"); DispList(ha); printf(\" 原集合B: \"); DispList(hb); sort(ha); sort(hb); Delete(ha); Delete(hb); printf(\" 有序集合A: \"); DispList(ha); printf(\" 有序集合A: \"); DispList(ha); Union(ha,hb,hc); printf(\" 集合的并C: \"); DispList(hc); InterSect(ha,hb,hc); printf(\" 集合的交C: \"); DispList(hc); Subs(ha,hb,hc); printf(\" 集合的差C: \"); DispList(hc); DestroyList(ha); DestroyList(hb); DestroyList(hc); return 0; } 8 / 35 2.流程图 开始 输入数据 尾插法建立单链表 将输入数据从小到大排序 删除表中重复数据 求集合的交集 求集合的并集 求集合的差集 结束 9 / 35 五.程序运行结果 [测试数据] 1、set1={3, 8, 5, 8,11},set2={22, 6, 8, 3, 15,11,20 } set1∪set2= set1∩set2= set1-set2= 2、其中一个集合为空集 3、两个集合都是空集 4、创建集合时有重复数据的情况 测试1 测试2 10 / 35 测试3 测试4 集合元素用尾插法建立的单链表进行存储,原集合进行排序并删除重复元素后进行集合的相关运算。 六. 所采用存储结构的优缺点及理由 优点: 1.长度不固定,可以任意增删。 2.存储空间不连续,数据元素之间使用指针相连,每个数据元素只能访问周围的一个元素(根据单链表还是双链表有所不同)。 3.存储密度小,因为每个数据元素,都需要额外存储一个指向下一元素的指针(双链表则需要两个指针)。 4.在特定的数据元素之后插入或删除元素,不涉及到其他元素的移动,因此时间复杂度为 O(1)。 11 / 35 缺点: 要访问特定元素,只能从链表头开始,遍历到该元素,时间复杂度为 O(n)。 采取单链表的理由: 由于集合运算有大量的删除、插入元素的操作,且数组的长度较短,无需访问大量元素,无随机查找元素需求,时间复杂度较顺序表而言更加具有优势,综合考虑,相比顺序表,单链表更适合进行集合的交并差运算。 12 / 35 第二题 一.题目2(栈、串) 利用栈实现算术表达式的求值。假设表达式中含有一位整数,以及+、-、*、/、(、)。但不受此限制。 二.题目功能描述 (1).表达式以字符串形式输入,并以‘#’开始和结束(‘#’也作为算法来处理)。 如输入:#6+3*(9-7)-8/2# (2).能够有效判别表达式的输入格式是否有误(如缺失操作数、非法算符、括 号不匹配等错误),若输入格式错误,输出错误提示。 [测试数据] 1、#6+3*(9-7)-8/2# 2、#(8-2)/(3-1)*(9-6)# 3、#5+a*(8-4)/2# 4、#5+(7-3)*6# 三.概要设计 输入表达式 判断表达式是否有误 输出结果 四.程序的源代码、注释以及流程图 1.源代码和注释 #include typedef struct { char data[50]; int top; }stack;//顺序栈类型定义 void newstack(stack *&S)//初始化顺序栈 { S=(stack*)malloc(sizeof(stack)); S->top=-1; } 13 / 35 int push(stack*S,char e)//进栈 { if(S->top>49) { printf(\"in error!\\n\"); return 0; } else { S->data[++S->top]=e; return 1; } } int empty(stack*S)//判断栈空 { if(S->top<0) return 1; else return 0; } char pop(stack*S)//出栈 { char e; if(empty(S)) { printf(\"out error!\\n\"); return 0; } else { e=S->data[S->top--]; return e; } } int isnum(char e)//判断是否为数字 { if(e>='0'&&e<='9') return 1; else return 0; } int num(char e)//字符型转换为整型{ int n; 14 / 35 n=e-48;//ASCII码 return n; } char nonum(int n)//整型转换为字符型 { char e; e=n+48;//ASCII码 return e; } int correct(char s[])//判断括号是否匹配 { stack *S; newstack(S); int flag=1,i=1; while(s[i]!='#'&&flag) { if(s[i]=='(') push(S,s[i]); if(s[i]==')') if(pop(S)!='(') flag=0; i++; }//最先遇到的后括号前必定是与之对应的前括号,如若匹配不成功,则flag记为0,即括号不匹配 if(!empty(S)) flag=0; return flag; } int rank(char a,char b)//判断运算符优先级,不包括括号的优先级 { if((a=='*'||a=='/')&&(b=='+'||b=='-')) return 1; else return 0; } void trans(char s1[],char s2[])//中缀表达式转换为后缀表达式,s1为中缀表达式,s2为后缀表达式 { int i=1,j=0; char e; stack *S; newstack(S); while(s1[i]!='#') 15 / 35 { if(isnum(s1[i]))//是数字直接输出 s2[j++]=s1[i]; else//运算符号,括号的处理 { if(s1[i]=='(')//前括号直接入栈 push(S,s1[i]); if(s1[i]==')')//遇到后括号输出栈顶运算符直至遇到左括号,左括号出栈但不输出 while((e=pop(S))!='(') s2[j++]=e; if(s1[i]=='+'||s1[i]=='-'||s1[i]=='*'||s1[i]=='/')//栈空入栈,栈顶为左括号入栈,优先级高于栈顶运算符入栈,否则出栈并再次判断,实际实现时先进行是否出栈判断 { while(!(empty(S)||S->data[S->top]=='('||rank(s1[i],S->data[S->top]))) s2[j++]=pop(S); if((empty(S)||S->data[S->top]=='('||rank(s1[i],S->data[S->top]))) push(S,s1[i]); } if(!(s1[i]=='+'||s1[i]=='-'||s1[i]=='*'||s1[i]=='/'||s1[i]=='('||s1[i]==')')) printf(\"非法运算符!\\n\"); //除过四则运算和括号(+、-、*、/、(、))外,其余判定为非法运算符 } i++; } while(!empty(S))//表达式读毕将栈内运算符一一输出 s2[j++]=pop(S); s2[j]='\\0';//字符串结尾 } int workout(char s2[])//计算后缀表达式的值 { int a,b,i=0; stack *S; newstack(S); while(s2[i]!='\\0') { if(isnum(s2[i]))//数字入栈等待运算符操作 push(S,s2[i++]); else { b=num(pop(S)); 16 / 35 a=num(pop(S));//字符型转为整型进行运算 switch(s2[i++]) { case'+': a=a+b; push(S,nonum(a)); break; case'-': a=a-b; push(S,nonum(a)); break; case'*': a=a*b; push(S,nonum(a)); break; case'/': a=a/b; push(S,nonum(a)); break;//运算后转为字符型入栈 default: printf(\"error!\"); } } } if(S->top!=0)//缺少运算符时,输出提示,但仍然带回栈顶元素的值作为运算结果输出 { printf(\"缺少运算符!得到中间结果!\\n\"); } a=num(pop(S));//字符型转为整形带入返回值 return a; } int main() { int ans; char s1[80],s2[80]; printf(\"输入表达式:\"); gets(s1); if(!(correct(s1))) { printf(\"括号不匹配!\"); return 0; } else { trans(s1,s2); printf(\"后缀表达式:\"); puts(s2); ans=workout(s2); printf(\"ans=%d\\n\ system(\"pause\"); return 0; } } 2.流程图 17 / 35 开始 实现顺序栈的基本运算 输入表达式 是 是否为数字 否 直接输出 判断括号是否匹配 否 是 不匹配,则flag=0 判断符号优先级 18 / 35 判断非法运算符 输出栈内运算符 计算后缀表达式的值 结束 19 / 35 5.数据输入输出 20 / 35 此题运用了栈这种结构来解决问题,数据以字符串形式输入,栈是一种后进先出的线性表,方便进行比对,对有效判别表达式的输入格式是否有误有很大帮助,输入数据有序进行计算,是这个存储结构很大的优势。 6.栈的优缺点: 优点:连续存储,空间利用率高 缺点:不方便数据的增删 采用顺序栈更方便进行比对,对有效判别表达式的输入格式是否有误有很大帮助,输入数据有序进行计算,连续存储,空间利用率高。 21 / 35 第六题 一. 题目6(哈夫曼树的编/译码器) [问题描述] 利用哈夫曼编码进行通讯可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统将待传数据进行预先编码;在接受端将传来的数据进行解码(复原)。对于可以双向传输的信道,每端都要有一个完整的编/译码系统。试为这样的信息收发站写一个哈夫曼的编译码系统。(难易程度:高) [实验目的] 1、通过哈夫曼树的定义,掌握构造哈夫曼树的意义。 2、掌握构造哈夫曼树的算法思想。 3、通过具体构造哈夫曼树,进一步理解构造哈夫曼树编码的意义。 [实验内容及要求] 1、从终端读入字符集大小为n(即字符的个数),逐一输n个字符和相应的n个权值(即字符出现的频度),建立哈夫曼树,将它存于文件hfmtree中。并将建立好的哈夫曼树以树或凹入法形式输出;对每个字符进行编码并且输出。 2、利用已建好的哈夫曼编码文件hfmtree,对对已编码的正文进行译码,输出译码后的正文。 3、采用文本文件存放文本,先统计文本中的每个字符出现的频率,然后再建立哈夫曼树,并进行编码和译码。 [测试数据] 1、用下表给出的字符集和频度的实际统计数据建立哈夫曼树。 M N 字符 A B C D E F G H I J K L 频度 64 13 22 32 103 21 15 47 57 1 5 32 20 57 字符 O P Q R S T U V W X Y Z 空格 频度 63 15 1 48 51 80 23 8 18 1 16 1 168 并实现以下报文的译码和输出:THISPROGRAMISMYFAVORITE 2、采用文本文件存放文本,测试实验内容及要求的第3项。 二. 题目功能描述 利用哈夫曼编码进行通讯可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统将待传数据进行预先编码;在接受端将传来的数据进行解码(复原)。对于可以双向传输的信道,每端都要有一个完整的编/译码系统。 三. 概要设计图 22 / 35 输出解码 输入频率统计词频读取文件 压缩 转码压缩查找位置 选择两个最小下标 构造哈夫曼树 四. 源代码及相应的流程图 1.程序源代码: #include