数据结构课程设计.docx
- 文档编号:14490097
- 上传时间:2023-06-23
- 格式:DOCX
- 页数:18
- 大小:66.06KB
数据结构课程设计.docx
《数据结构课程设计.docx》由会员分享,可在线阅读,更多相关《数据结构课程设计.docx(18页珍藏版)》请在冰点文库上搜索。
数据结构课程设计
《数据结构》
课
程
设
计
【题目一】
约瑟夫环
【问题描述】
约瑟夫(Joseph)问题的一种描述是:
编号为1,2,…,n的个人按顺时针方向围坐一圈,没人持有一个密码(正整数)。
一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。
报m的人出列,将他的密码作为新的m值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直至所有人全部出列为止。
试设计一个程序求出出列顺序。
【基本要求】
利用单向循环链表存储结构模拟此过程,按照出列的顺序印出各人的编号。
【测试数据】
M的初值为20;n=7,7个人的密码一次为:
3,1,7,2,4,8,4,首先m值为6(正确的出列顺序应为6,1,4,7,2,3,5)。
【程序分析】
一、需求分析
1.本演示程序中,利用单向循环链表存储结构存储约瑟夫环数据(既n个人的编号和密码)初始密码和每个人的密码为1~20,人数为1~7,先输入初始密码m,再输入人数n,接下来输入n个正整数,数与数之间用逗号隔开,作为这n个人的密码。
2.演示程序以用户和计算机的对话方式执行,即在计算机终端上显示"提示信息"之后,由用户在键盘上输入演示程序中需要输入的数据,运算结果显示在其后。
3.程序执行的命令包括:
1)构造单向循环链表;
2)提供用户从键盘输入,Joseph约瑟夫环的必要数据,并显示出列顺序。
4.测试数据
m的初值为20;n=7,7个人的密码依次为:
3,1,7,2,4,8,4,首先m值为6(正确的出列顺序为6,1,4,7,2,3,5)。
二、概要设计
1.单向循环链表的抽象数据类型定义为:
ADTList{
数据对象:
D={ai|ai∈正整数,I=1,2,......,n,n≥0}
数据关系:
R1={<ai-1,ai>|,ai-1,ai∈D,I=1,2,......,n}
基本操作:
InitList(&L)
操作结果:
构造一个最大长度ms内容为空的有序表L。
ClearList(&L)
初始条件:
线性表L已经存在。
操作结果:
将L重置为空表。
EmptyList(L)
初始条件:
线性表L已经存在。
操作结果:
若L为空表返回TRUE,否则返回FALSE。
ListLength(L)
初始条件:
线性表L已经存在。
操作结果:
返回L中数据元素个数。
GetElem(L,pos,&e)
初始条件:
线性表L已经存在,1≤i≤ListLength(L)。
操作结果:
用e返回L中第i个数据元素的值。
LocateElem(L,e)
初始条件:
线性表L已经存在。
操作结果:
返回L中第1个与e相同的元素的位序。
若不存在返回0。
ListInsert(L,i,e)
初始条件:
线性表L已经存在。
操作结果:
在L中的第i个元素的位置之前插入新元素e,L的长度加1。
ListDelete(L,pos,e)
初始条件:
线性表L已经存在,1≤i≤ListLength(L)。
操作结果:
删除L的第i个数据元素,并用e返回其值,L的长度减1。
ListTraverse(L)
初始条件:
线性表L已经存在。
操作结果:
依次对L的每个数据元素进行访问。
}ADTSqList
本程序包含以下模块:
(1)主程序模块:
其中又包括建立线性表和模拟约瑟夫环处理两大过程
voidmain()
{
初始化;
输入数据;
执行功能;
显示结果;
}
(2)线性表模块:
实现线性表的抽象数据类型
(3)元素结构单元模块:
定义线性表每个元素的结构
(2)各功能模块——实现顺序表的各项功能。
各模块的调用关系:
主程序
↓
各功能模块
三、详细设计
#include
#include
typedefstructdata
{//定义一个结构体"data"
intnum;//用于存放人的序号
intval;//用于存放密码
}typedata;
typedefstructnode
{//定义一个结构体(结点),其中包含一个数据域和一个指针域
typedatadata;//结构体的嵌套
structnode*next;
}listnode;
typedeflistnode*linklist;
linklisthead;
voidmain()//进入主函数
{
intn,i,b,m,j;
linklisthead=(listnode*)malloc(sizeof(listnode));//申请一个空间
listnode*p,*q;//定义两个可以指向结点的指针
printf("输入总人数:
");
scanf("%d",&n);
q=head;//用指针q指向头结点
for(j=1;j<=n;j++)//本次循环主要是将每一个人的数据(包括序号、密码)存入循环链表中
{
printf("请输入第%d号同学的密码:
",j);
scanf("%d",&b);
q->next=(listnode*)malloc(sizeof(listnode));//将头结点的next域指向刚生成的一个结点
q=q->next;
q->data.val=b;//输入密码
q->data.num=j;//输入序号
q->next=head->next;
}//将尾结点的next域指向第一个结点,构成循环链表
printf("请任意输入一个数m:
");
scanf("%d",&m);
if(m<=0)printf("输入错误");
do{
i=1;
while(i!
=m)
{//将q指针指向所要输出的结点的前一结点
q=q->next;
i++;
}
p=q->next;//p指向输出结点
q->next=p->next;//将输出结点的前结点的next域指向输出结点的后结点
printf("号码:
%d\t密码:
%d\n",p->data.num,p->data.val);//输出
m=p->data.val;//取得输出结点的密码
free(p);
}
while(q->next!
=q);//剩最后一个结点时结束
printf("号码:
%d\t密码:
%d\n",q->data.num,q->data.val);//最后一个结点
free(head);//释放头结点
free(q);//释放最后一个结点
printf("约瑟夫环结束\n");
}//程序结束
四、调试分析:
1、调试过程中遇到的问题是如何解决的以及对设计与实现的回顾讨论和分析:
调试时没有运行结果,后来发现没有在主程序里加上getch()函数,加上后正确。
m的初值(上限值)为20;n=7,7个人的密码依次为:
3,1,7,2,4,8,4,
m值为6结果为:
6,1,4,7,2,3,5。
2、算法的时空分析:
在init()中共循环了n次。
在约瑟夫函数中最坏的情况下要n次,所以时间复杂度为0(n)
改进方法:
我认为要使用双向循环链表可以更好的降低事件复杂度,而循环链表就能很好的节省空间。
五、测试结果
【题目二】
Huffman编/译码器
【问题描述】
利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。
但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码(复原)。
对于双工信道(即可以双向传输信息的通道),每端都需要一个完整的编/译码系统。
试为这样的信息收发站写一个哈夫曼的编/译码系统。
【基本要求】
一个完整的系统应具有以下功能:
(1)I:
初始化。
从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树,并将它存于文件hfmTree中。
(2)E:
编码。
利用以建好的哈夫曼树(如不在内存,则从文件hfmTree中读入),对文件ToBeTran中的正文进行编码,然后将结果存入文件CodeFile中。
(3)D:
译码。
利用已建好的哈夫曼树将文件CodeFile中的代码进行译码,结果存入文件TextFile中。
(4)P:
印代码文件。
将文件CodeFile以紧凑格式显示在终端上,每行50个代码。
同时将此字符形式的编码文件写入文件CodePrin中。
(5)T:
印哈夫曼树。
将已在内存中的哈夫曼树以直观的方式(树或凹入表形式)显示在终端上,同时将此字符形式的哈夫曼树写入文件CodePrin中。
【数据测试】
(1)利用下面的数据调试程序:
已知某系统在通信联络中只可能出现8种字符,其概率分别为0.05,0.29,0.07,0.08,0.14,0.23,0.03,0.11。
(2)用下表给出的字符集和频度的实际统计数据建立哈夫曼树,并实现以下报文的编码和译码:
“THISPROGRAMISMYFAVORITE”。
字符
空格
A
B
C
D
E
F
G
H
I
J
K
L
M
频度
186
64
13
22
32
103
21
15
47
57
1
5
32
20
字符
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
频度
57
63
15
1
48
51
80
23
8
18
1
16
1
【程序分析】
一、需求分析
1.本演示程序中,利用数组存储结构存储哈夫曼数数据(即字符使用频度),先输入总字符个数为,再输入字符使用频度,数与数之间用空格或逗号隔开。
2.演示程序以用户和计算机的对话方式执行,即在计算机终端上显示"提示信息"之后,由用户在键盘上输入演示程序中需要输入的数据,运算结果显示在其后。
3.程序执行的命令包括:
1)构造哈夫曼树;
2)提供用户从键盘输入哈夫曼树的必要数据;
3)对哈夫曼树进行编译,并显示出哈夫曼编码。
二、概要设计
1.树的抽象数据类型定义为:
1、树节点的抽象数据结构:
template
public:
TreeNode(constT&);//拷贝构造函数
virtual~TreeNode(){};//析构函数
boolisLeaf();//如果结点是叶,返回true
TValue();//返回结点的值
TreeNode
TreeNode
voidsetValue(T&);//设置结点的值
voidsetChild(TreeNode
voidsetSibling(TreeNode
voidInsertFirst(TreeNode
voidInsertNext(TreeNode
};
2、树/森林的抽象数据结构:
template
public:
Tree();//构造函数
virtual~Tree();//析构函数
TreeNode
voidCreateRoot(constT&rootValue);//创建树中的根结点,使根结点元素的值为rootValue
boolisEmpty();//判断是否为空树,如果是则返回true
TreeNode
TreeNode
voidDeleteSubTree(TreeNode
voidRootFirstTraverse(TreeNode
voidRootLastTraverse(TreeNode
voidWidthTraverse(TreeNode
};
本程序包含以下模块:
(1)主程序模块:
其中又包括建立哈夫曼树和哈夫曼编码的编译两大过程
voidmain()
{
初始化;
输入数据;
执行功能;
显示结果;
}
(2)哈夫曼树模块:
实现树的抽象数据类型
(3)元素结构单元模块:
定义哈夫曼树每个元素的结构
(2)各功能模块——实现哈夫曼树的的各项功能。
各模块的调用关系:
主程序
↓
各功能模块
三、详细设计
#include
#include
#include
#include
#include
typedefstruct
{
floatweight;//结点权值
unsignedintparent,lchild,rchild;//结点的父指针,左右孩子指针
}HTNode,*HuffmanTree;//动态分配数组存储哈夫曼树
typedefchar**HuffmanCode;//动态分配数组存储哈夫曼编码表
voidCreateHuffmanTree(HuffmanTree&,float*,int);//生成哈夫曼树
voidHuffmanCoding(HuffmanTree,HuffmanCode&,int);//对哈夫曼树进行编码
voidPrintHuffmanCode(HuffmanCode,float*,int);//显示哈夫曼编码
voidSelect(HuffmanTree,int,int&,int&);//在数组中寻找权值最小的两个结点
voidmain()
{
HuffmanTreeHT;//哈夫曼树HT
HuffmanCodeHC;//哈夫曼编码表HC
intn,i;//n是哈夫曼树叶子结点数
float*w;//w存放叶子结点权值
charj='y';
while(j!
='N'&&j!
='n')
{
printf("请输入字符数目:
");
scanf("%d",&n);//输入字符数目
if(n<=1)
{
printf("该数不合理!
\n");continue;
}
w=(float*)malloc(n*sizeof(unsignedint));//开辟空间存放权值
printf("请输入各字符出现的次数/权值:
\n");
for(i=0;i CreateHuffmanTree(HT,w,n);//生成哈夫曼树 HuffmanCoding(HT,HC,n);//进行哈夫曼编码 PrintHuffmanCode(HC,w,n);//显示哈夫曼编码 printf("哈夫曼树构造完毕! "); scanf("%c",&j); } } voidCreateHuffmanTree(HuffmanTree&HT,float*w,intn) {//w存放n个结点的权值,将构造一棵哈夫曼树HT inti,m; ints1,s2; HuffmanTreep; if(n<=1)return; m=2*n-1;//n个叶子结点的哈夫曼树,有2*n-1个结点 HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));//开辟2*n各结点空间 for(p=HT+1,i=1;i<=n;++i,++p,++w)//进行初始化 { p->weight=*w; p->parent=0; p->lchild=0; p->rchild=0; } for(;i<=m;++i,++p) { p->weight=0; p->parent=0; p->lchild=0; p->rchild=0; } for(i=n+1;i<=m;++i)//建哈夫曼树 { Select(HT,i-1,s1,s2); //从HT[1...i-1]中选择parent为0且weight最小的两个结点,其序号分别为s1和s2 HT[s1].parent=i;HT[s2].parent=i;//修改s1和s2结点的父指针parent HT[i].lchild=s1;HT[i].rchild=s2;//修改i结点的左右孩子指针 HT[i].weight=HT[s1].weight+HT[s2].weight;//修改权值 } } voidHuffmanCoding(HuffmanTreeHT,HuffmanCode&HC,intn) {//将有n个叶子结点的哈夫曼树HT进行编码,所编的码存放在HC中 //方法是从叶子到根逆向求每个叶子结点的哈夫曼编码 inti,c,f,start; char*cd; HC=(HuffmanCode)malloc((n+1)*sizeof(char*));//分配n个编码的头指针向量 cd=(char*)malloc(n*sizeof(char));//开辟一个求编码的工作空间 cd[n-1]='\0';//编码结束符 for(i=1;i<=n;++i)//逐个地求哈夫曼编码 { start=n-1;//编码结束位置 for(c=i,f=HT[i].parent;f! =0;c=f,f=HT[f].parent)//从叶子到根逆向求编码 if(HT[f].lchild==c)cd[--start]='0';//若是左孩子编为'0' elsecd[--start]='1';//若是右孩子编为'1' HC[i]=(char*)malloc((n-start)*sizeof(char));//为第i个编码分配空间 strcpy(HC[i],&cd[start]);//将编码从cd复制到HC中 } free(cd);//释放工作空间 } voidPrintHuffmanCode(HuffmanCodeHC,float*w,intn) {//显示有n个叶子结点的哈夫曼树的编码表 inti; printf("HuffmanCodeis: \n"); for(i=1;i<=n;i++) { printf("%3d---",w[i-1]); puts(HC[i]); } printf("\n"); } voidSelect(HuffmanTreeHT,intt,int&s1,int&s2) {//在HT[1...t]中选择parent不为0且权值最小的两个结点,其序号分别为s1和s2 inti,m,n; m=n=10000; for(i=1;i<=t;i++) { if(HT[i].parent==0&&(HT[i].weight if(m { n=HT[i].weight;s2=i; } else { m=HT[i].weight;s1=i; } } if(s1>s2)//s1放较小的序号 { i=s1;s1=s2;s2=i; } } 四、调试结果 五、调试分析: 1.调试过程中遇到的问题是如何解决的以及对设计与实现的回顾讨论和分析: 刚开始权值只能为正整数,是因为将权值设为了int型,后来将其改为float型,结构正确。 2.算法的时空分析: 在逐个求哈夫曼编码时,循环了n! 次。 在哈夫曼树最坏的情况下要2*n! 次,所以时间频度为2*n! 【参考文献】 1.严蔚敏,吴伟民.数据结构.清华大学出版社,2005.11 2.谭浩强.C语言程序设计.清华大学出版社,2005.11 3.范辉.VisualC++程序设计.高等教育出版社 【总结】 通过这次课程设计,我更好的了解到了约瑟夫环以及Huffman编/译码器的应用。 并且在这次课程设计中,更好的锻炼了自己的动手能力,并且让我温习了C语言,对C++得到了更多的练习。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 数据结构 课程设计