赫夫曼树的应用.docx
《赫夫曼树的应用.docx》由会员分享,可在线阅读,更多相关《赫夫曼树的应用.docx(18页珍藏版)》请在冰豆网上搜索。
赫夫曼树的应用
题目:
赫夫曼树的应用
课程名称:
《数据结构》课程设计
指导教师:
专业:
班级:
学号:
姓名:
学号:
姓名:
学号:
姓名:
学号:
姓名:
完成时间:
目录
一、前言2
二、程序设计目的3
三、需求分析3
四、概要设计3
1、主要需要用到的主要数据结构3
2、算法流程分析4
五、源程序和运行结果5
1.源程序如下5
2、运行结果11
六、调试分析14
1.测试的结果14
2.时间复杂度分析14
3.各模块在设计和调试时存在问题14
4.算法的改进设想14
七、设计心得和总结15
八、参考书籍15
一、前言
赫夫曼编码(HuffmanCoding)是一种编码方式,赫夫曼编码是可变字长编码(VLC)的一种。
赫夫曼压缩是个无损的压缩算法,一般用来压缩文本和程序文件。
赫夫曼压缩属于可变代码长度算法一族。
意思是个体符号(例如,文本文件中的字符)用一个特定长度的位序列替代。
因此,在文件中出现频率高的符号,使用短的位序列,而那些很少出现的符号,则用较长的位序列。
赫夫曼编码的应用很广泛,利用赫夫曼树求地的二进制编码称为赫夫曼编码。
赫夫曼树中从根到每个叶子都有一条路径,对路径上的各分支约定:
指向左子树的分支表示“0”码,指向右子树的分支表示“1”码,取每条路径上的“0”或“1”的序列作为对应的编码,这就是赫夫曼编码。
我们在对一些问题进行求解时,会发现有些问题很难找到规律,或者根本无规律可寻。
对于这样的问题,可以利用计算机运算速度快的特点,先搜索查找所有可能出现的情况,再根据题目条件从所有可能的情况中,删除那些不符合条件的解。
由赫夫曼算法的定义可知,初始森林中共有n棵只含有根结点的二叉树。
算法的的第二步是:
算法的的第二步是:
将当前森林中的两棵根结点权值最小的二叉树,合并成一棵新的二叉树,每合并一次,森林中就减少一棵树,产生一个新结点。
则要进行n-1次合并,所以共产生n-1个新结点。
由此可知,最终求得的赫夫曼树中一共有2n-1个结点。
其中,n个结点是初始森林中的n个孤立结点。
并且赫夫曼树中没有度数为1的分支的结点。
采用赫夫曼编码方案,即应用赫夫曼树构造使电文的编码总长最短的编码方案。
二、程序设计目的
1.熟悉掌握C语言的基础知识和技能;
2.学习利用计算机语言实现赫夫曼树构造的算法;
3.了解利用赫夫曼树对给定权值的字符的赫夫曼编码的算法思想。
三、需求分析
1、打开一个文本文件,用一个指针指向它,指针变量为*pf
2、对其进行字母频率统计,把字母和字母的频率放到结构体syrs1[]中
3、用Huffman算法建立Huffman树以及每个字母的Huffman码(Huffman数以及Huffman码两张表,均要求输出,最好能在文本中输出)
4、把该文本文件的英文文章利用Huffman树编码生成二进制文件(该文件要求输出)
5、打开该二进制文本文件,对其解码,仍然利用刚才的Huffman树进行解码
6、输入,输出要求都是读取文件。
四、概要设计
1、主要需要用到的主要数据结构
a、赫夫曼树的数据结构
typedefstruct
{
chardata;
intweight;
intparent;
intlchild;
intrchild;
}HuffmanTree[M];
b、赫夫曼编码的数据结构
typedefstruct
{
chardata;
charbits[N];
}HuffmanCode[N];
c、字符串存储单元的数据结构
typedefstructstr
{
chardata;
charnum;
//intnum;
}str;
2、算法流程分析
a、建立赫夫曼树的数据结构、赫夫曼编码的数据结构和字符串存储单元的数据结构,字符串存储单元的数据结构是用来存储字母和对应的权值。
b、调用intread(strs2[])函数,取读文本ywq1.txt并统计这里出现字符的个数(字母区分大小写、空格键和各种标点符号),再统计该字符在文本中出现的频率(也就是所谓的权值)存放在结构体strs2[]中,返回字母个数k,打印字母的和对应的权值。
c、调用CrtHuffmanTree(ht,s2,k)函数来创建赫夫曼数,首先初始化节点并给其赋值0,利用for的循环语句并调用SelectMin函数,查找哈夫曼链表中两个权值最小的来建立赫夫曼树,打印赫夫曼树各节点的权值和它的左右孩子。
d、调用CrtHuffmanCode(ht,hc,k)利用建立好的哈夫曼树对文本文本ywq1.txt进行编码,并把编译的赫夫曼编码储存在文本ywq2.txt中,并且打印出来文本的赫夫曼编码。
e、通过调用DecodHuffmanCode(ht,k)将文本ywq2.txt字符的赫夫曼码翻译为字符,这就是所谓的反译码,把翻译的英语文章存储在文本ywq3.txt并且存储,并且在对话执行框中打印出来。
五、源程序和运行结果
1.源程序如下
#include
#include
#include
#defineN100
#defineM2*N-1
typedefstruct//定义哈夫曼树存储节点结构体类型
{
chardata;
intweight;
intparent;
intlchild;
intrchild;
}HuffmanTree[M];
typedefstruct//定义哈夫曼编码结构体类型
{
chardata;
charbits[N];
}HuffmanCode[N];
typedefstructstr//定义字符串存储单元结构体类型
{
chardata;
charnum;
//intnum;
}str;
intread(strs2[])
{
FILE*fp;
charch;
inti,k;
strs1[128];
for(i=0;i<=128;i++)
{
s1[i].num=0;s1[i].data=0;
s2[i].num=0;s2[i].data=0;
}
if((fp=fopen("ywq1.txt","r"))==NULL)
{
printf("\n库文件不存在!
");
exit
(1);
}
printf("\n读取字符串为:
\n");
ch=fgetc(fp);
while(!
feof(fp))//统计字符频率
{
printf("%c",ch);
s1[ch].num++;
s1[ch].data=ch;
ch=fgetc(fp);
}
fclose(fp);
for(i=1,k=1;i<=128;i++)
{
if(s1[i].num!
=0)
{
s2[k].num=s1[i].num;
s2[k].data=s1[i].data;
k++;
}
}
printf("\n\n统计结果为(字符频率):
\n");
for(i=1;i{
printf("<%c%d>",s2[i].data,s2[i].num);
}
printf("(共%d种字符)\n",k-1);
returnk;
}
voidSelectMin(HuffmanTreeht,inti,int*p1,int*p2)//查找哈夫曼链表中两个权值最小的节点
{
intj,min1,min2;
min1=min2=-1;
for(j=1;j<=i;j++)
{
if(ht[j].parent==0)
{
if(ht[j].weight{
if(min1!
=-1)
{min2=min1;*p2=*p1;}
min1=ht[j].weight;*p1=j;
}
elseif(ht[j].weight{
min2=ht[j].weight;
*p2=j;
}
}
}
}
voidCrtHuffmanTree(HuffmanTreeht,strs[],intn)//创建哈夫曼树
{
inti,m,p1,p2;
for(i=1;i{
ht[i].data=s[i].data;
ht[i].weight=s[i].num;
ht[i].parent=0;
ht[i].lchild=0;
ht[i].rchild=0;
}
m=2*n-3;
for(i=n;i<=m;i++)
{
ht[i].data=0;
ht[i].weight=0;
ht[i].parent=0;
ht[i].lchild=0;
ht[i].rchild=0;
}
for(i=n;i<=m;i++)
{
SelectMin(ht,i-1,&p1,&p2);//调用SelectMin函数
ht[i].weight=ht[p1].weight+ht[p2].weight;
ht[p1].parent=i;ht[p2].parent=i;
ht[i].lchild=p1;ht[i].rchild=p2;
}
}
voidCrtHuffmanCode(HuffmanTreeht,HuffmanCodehc,intk)//利用建立好的哈夫曼树对字符串进行编码
{
intc,p,i;
charcd[N+1];
intstart;
for(i=1;i{
hc[i].data=ht[i].data;
start=k-1;
cd[start]='\0';
c=i;
while((p=ht[c].parent)!
=NULL)
{
cd[--start]=(ht[p].lchild==c)?
'0':
'1';//左分支为0,右分支为1
c=p;
}
strcpy(hc[i].bits,&cd[start]);
}
printf("\n\n每个字符对应的编码为:
\n");
for(i=1;iprintf("<%d%c%s>\n",i,hc[i].data,hc[i].bits);
}
voidWriteToFile(HuffmanCodehc,intn)//将编码结果存储在文件文件ywq2.txt中
{
FILE*fp1,*fp2;
charch;
inti;
if((fp1=fopen("ywq1.txt","r"))==NULL)
{
printf("\n文件不存在!
");
exit
(1);
}
if((fp2=fopen("ywq2.txt","w"))==NULL)
{
printf("\n文件不存在!
");
exit
(1);
}
ch=fgetc(fp1);
printf("\n编码结果为:
");
while(ch!
=EOF)
{
for(i=1;iif(ch==hc[i].data)
{
fputs(hc[i].bits,fp2);
printf("%s",hc[i].bits);
}
ch=fgetc(fp1);
}
fclose(fp1);
fclose(fp2);
printf("\n");
}
voidDecodHuffmanCode(HuffmanTreeht,intn)//码结果进行译码,并将结果存储在文件ywq3中
{
FILE*fp1,*fp2;
charch;
intp,k;
if((fp1=fopen("ywq2.txt","r"))==NULL)
{
printf("\n文件不存在!
");
exit
(1);
}
if((fp2=fopen("ywq3.txt","w"))==NULL)
{
printf("\n文件未能创建!
");
exit
(1);
}
p=k=2*n-3;
ch=fgetc(fp1);
printf("译码为:
");
while(ch!
=EOF)
{
if(ch=='0')p=ht[p].lchild;
elseif(ch=='1')p=ht[p].rchild;
if(ht[p].data!
=0)
{
printf("%c",ht[p].data);
fputc(ht[p].data,fp2);
p=k;
}
ch=fgetc(fp1);
}
printf("\n");
fclose(fp1);fclose(fp2);
}
voidcompare(intk)
{
FILE*fp1,*fp2;
chars1[N],s2[N];
inti=1,j=1;
printf("\n\n编译前后结果的比较:
");
if((fp1=fopen("ywq1.txt","rt"))==NULL)
{
printf("\n打开文件失败!
\n");
exit
(1);
}
printf("\n\n原文件ywq1中的字符为:
");
for(i=1;(s1[i]=fgetc(fp1))!
=EOF;i++)
printf("%c",s1[i]);
fclose(fp1);
if((fp2=fopen("ywq3.txt","rt"))==NULL)
{
printf("\n打开文件失败!
\n");
exit
(1);
}
printf("\n文件ywq3中的字符为:
");
for(i=1;(s2[i]=fgetc(fp2))!
=EOF;i++)
printf("%c",s2[i]);
fclose(fp2);
while(j{
if(s1[j]==s2[j])
j++;
else
{
printf("\n编码失败!
\n");
break;
}
}
if(j==k)
printf("\n前后数据一致,编码成功!
\n");
}
voidmain()
{
inti,k;
intj=1;
HuffmanTreeht;
HuffmanCodehc;
strs2[128];
printf("\n-------------------------------哈夫曼编码译码器---------------------------------");
k=read(s2);
getchar();
CrtHuffmanTree(ht,s2,k);
CrtHuffmanCode(ht,hc,k);
WriteToFile(hc,k);
getchar();
printf("\n\n");
printf("建立的哈夫曼树为:
");
printf("\nnumber\tdata\tweight\tlchild\trchild\tparent");
for(i=1;i{
printf("\n%d:
%c%d%d%d}
printf("\n\n");
DecodHuffmanCode(ht,k);
getchar();
compare(k);
//printf("\n\n按任意键退出...\n");
}
2、运行结果
注意:
在运行的时候,一直按【Enter】键执行
六、调试分析
1.测试的结果
请参考上面的运行结果。
2.时间复杂度分析
构造赫夫曼树:
T(n)=O(n)
建立赫夫曼表:
T(n)=O(n2)
进行译码:
T(n)=O(n)
赫夫曼编码T(n)=O(n³)
3.各模块在设计和调试时存在问题
a、怎样把一篇文章英语文章的字符个数统计出来,并且把权值给统计出来,最后是用什么来存储。
b、建立赫夫曼树,先找到权值最小的两个字符建立成赫夫曼树,并且写出赫夫曼码。
c、最大的难题是怎么把文本用赫夫曼码代替,就是把英语文章翻译成赫夫曼码,就是所谓的译码过程。
d、将译码后的文章打印出来
4.算法的改进设想
在这个算法中我觉得有个地方要改进,就是在编译和反编译的过程中,存储的那些赫夫曼码和英语文章在每次执行之后没有自己清除,我觉得这个地方要改进,还有一点这个算法在时间复杂度上面有点复杂,但是我没有找到好的方法,这是我觉得这个算法要改进的地方
七、设计心得和总结
八、参考书籍
《数据结构理论与实践》杨永斌.主编天津科学技术出版社