Huffman二叉树实验报告数据结构C语言.docx
《Huffman二叉树实验报告数据结构C语言.docx》由会员分享,可在线阅读,更多相关《Huffman二叉树实验报告数据结构C语言.docx(14页珍藏版)》请在冰豆网上搜索。
Huffman二叉树实验报告数据结构C语言
数 据 结 构 作 业 报 告
——Huffman二叉树实验报告
姓名:
江海强
班级:
070921班
学号:
07092007
上机时间:
2010-10-22
报告时间:
2010-10-26
摘要
1.实验目的
本实验是为了让我们深入了解Huffman二叉树,学会使用Huffman编码对数据进行无损压缩,最终能够灵活运用Huffman二叉树。
2.实验方法
利用递归的方法创建Huffman二叉树,且利用了二叉树的性质对字符串进行编码和译码。
3.实验结果
此程序是在C++环境中运行的。
由后面的运行出来的结果且由验证解码的结果可以得知,此程序是正确无误的。
由此我们还可以看出利用Huffman编码可以大大节省空间复杂度。
内容
一.问题重述
设计一个程序,首先读入一个ASCII文件,统计文档中字符出现的频度,并根据频度对每个字符生成Huffman编码。
需要打印出原始数据、每个字符对应的Huffman编码以及原文档的Huffman编码。
还要按照Huffman树对编码后的数据进行解码且验证解码的结果。
最后输出一些统计数据,如总编码长度、编码效率等。
二.算法描述
本程序除了运用一些条件语句,判断语句之外,主要是运用了二叉树的性质来设计程序的。
本程序利用二叉树来设计二进制的前缀编码。
约定了左分支表示字符'0',右分支表示字符'1',则可以从根结点到叶子结点的路径上分支字符组成的字符串作为该叶子结点字符的编码。
假设每种字符在输入的字符串中出现的次数为
,其编码长度为
,字符串中只有n种字符,则字符串总长为(即二叉树上带权路径的长度):
WPL=
当输入字符串jiang_hai_qiang时,
即可建立Huffman树,如下面两表所示,
根据此表可以建立如右图①的Huffman
二叉树的结构图
例如n的频率为2,q的频率为1,
这两个结点的共同parent结点的频率为
2+1=3。
编码表:
编码频率:
编码长度:
编码表:
编码频率:
编码长度:
j:
1110
1
4
g:
100
2
3
i:
110
3
3
_:
101
2
3
a:
00
3
2
h:
1111
1
4
n:
010
2
3
q:
011
1
3
表1
num
weight
parent
lchild
rchild
1
1
9
0
0
2
3
12
0
0
3
3
13
0
0
4
2
10
0
0
5
2
11
0
0
6
2
11
0
0
7
1
9
0
0
8
1
10
0
0
9
2
12
1
7
10
3
13
4
8
11
4
14
5
6
12
5
14
2
9
13
6
15
3
10
14
9
15
11
12
15
15
0
13
14
表2
由此可以算出WPL=2×3+3×(3+2+2+2+1)+4×(1+1)=42。
三.变量说明
全局变量A[]是用来存储字符的权值的。
weight代表的是该结点的权值。
程序中有m=2*n-1,是为Huffman二叉树开辟2n-1个结点。
在主函数中,m是用来记录输入字符串的个数的;n是用来记录有多少种字符的;a[]则是完整地记录输入的字符串;而b[]是记录输入字符串中的不同字符。
HT表示Huffman树;而HC表示Huffman编码。
其中还要说明的一些C++语句:
如cout<>a代表的输入语句;outfile<四.函数与思路说明
此程序有1个主函数和7个子函数。
其中子函数分别为Calculate(),Select(),ASCII1(),ASCII2(),HuffmanCoding(),SC_HuffmanCoding(),HuffmanDecoding()。
子函数Calculate()是用来计算权值的。
子函数Select()的作用是在HT[1,2,…i-1]选择parent为0且weight最小的两个结点,其序号分别为s1和s2。
子函数ASCII1()和ASCII2()分别是用来写入ASCII1和ASCII2文件的,这两个文件中分别存储Huffman二叉树的结构表和Huffman编码表。
子函数HuffmanCoding()主要是为赫夫曼二叉树编码,构造赫夫曼二叉树HT,并求出n个字符的赫夫曼编码HC。
其中此函数本身调用了子函数Select()和子函数ASCII1()。
在这个程序中,此子函数HuffmanCoding()为最重要的子函数。
它不但分配了Huffman二叉树结点的空间,且记录好每个结点的内容。
它还从叶子到根逆向求每个字符的赫夫曼编码。
SC_HuffmanCoding()这个子函数是用来输出字符串的赫夫曼编码的。
而最后的HuffmanDecoding()这个子函数,则是对编码后的数据进行解码。
五.程序执行结果
请任意输入一组字符:
jiang_hai_qiang
Huffman二叉树的结构:
numweightparentlchildrchild
11900
231200
331300
421000
521100
621100
71900
811000
921217
1031348
1141456
1251429
13615310
149151112
151501314
Huffman各字符的编码如下:
编码表:
编码频度:
j:
11101
i:
1103
a:
003
n:
0102
g:
1002
_:
1012
h:
11111
q:
0111
输入的字符串的Huffman编码:
11101100001010010111110011010101111000010100
请输入一串编码:
11101100001010010111110011010101111000010100
所得译码为:
jiang_hai_qiang
六.结论
由程序的运行结果和前面的讨论得知,运用Huffman树来编码解码是非常节约空间,减少复杂度的。
通过这次编程,可以让我们深入了解Huffman树,学会灵活运用Huffman树,也可以让我们深刻地认识到Huffman树的优点和作用。
七.编程中遇到的问题以及解决方法
在编程过程中,运用C语言不会读入ASCII文件,后来运用C++语言成功写入和读出文件。
刚刚开始编程是打算连空格也作为字符来存储在Huffman树节点中的,即在主函数中也定义了数组c[100],用gets(c)输入一串字符,再利用for语句把字符串分为一个一个字符存储在数组a[100]中的,可是这样子的运行结果会有乱码。
后来就直接用C++语句cin>>a来替换上述的语句了,虽然不能存储空格字符,但是只要不在中间输入空格,运行结果是正确的,此程序还是挺好的。
八.附录
#include
#include
#include
#include
#include
#include
usingnamespacestd;
intA[100];
typedefstruct
{
unsignedintweight;
unsignedintparent,lchild,rchild;
}HTNode,*HuffmanTree;
typedefchar**HuffmanCode;
voidCalculate(chara[],charb[],intm,intn,int*w)//计算权值
{
inti,j;
for(i=0;ifor(j=0;jif(b[i]==a[j])
A[i+1]=w[i+1]++;
}
voidSelect(HuffmanTreeHT,intn,int&s1,int&s2)
{
HT[0].weight=100;//输入权值一般不能超过次数
s1=0;s2=0;
for(inti=1;i<=n;++i)
if(HT[i].parent==0)
{
if(HT[i].weight{
if(HT[s2].weights2=i;
}
elseif(HT[i].weight}
}
voidASCII1(HuffmanTreeHT,intm)
{
inti;
ofstreamoutfile("ASCII1.txt");//写入ASCII1文件
outfile<<"numweightparentlchildrchild"<for(i=1;i<=m;i++)
{
outfile<
outfile<outfile<outfile<outfile<}
}
voidASCII2(charb[],HuffmanCodeHC,intn)
{
inti;
ofstreamoutfile("ASCII2.txt");//写入ASCII2文件
outfile<<"编码表:
"<<"\t"<<"编码频度"<for(i=1;i<=n;i++)
outfile<
"<}
//赫夫曼二叉树编码
//其中w存放n个字符的权值(均>0),构造赫夫曼树HT,并求出n个字符的赫夫曼编码HC
voidHuffmanCoding(HuffmanTree&HT,HuffmanCode&HC,int*w,intn)
{
intm,i,s1,s2,start;
unsignedc,f;
HuffmanTreep;
char*cd;
m=2*n-1;
HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));
for(p=HT+1,i=1;i<=n;++i,++p)//前n个结点初始化
{
p->weight=w[i];
p->parent=0;
p->lchild=0;
p->rchild=0;
}
for(;i<=m;++i,++p)//对从第n+1个顶点到第2n-1个顶点初始化
{
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[s1].parent=i;
HT[s2].parent=i;
HT[i].lchild=s1;
HT[i].rchild=s2;
HT[i].weight=HT[s1].weight+HT[s2].weight;
}
ASCII1(HT,m);
printf("\nHuffman二叉树的结构:
\n");
printf("numweightparentlchildrchild\n");
for(i=1;i<=m;i++)
{
printf("%d\t",i);
printf("%d\t",HT[i].weight);
printf("%d\t",HT[i].parent);
printf("%d\t",HT[i].lchild);
printf("%d\n",HT[i].rchild);
}
//---从叶子到根逆向求每个字符的赫夫曼编码---
HC=(HuffmanCode)malloc((n+1)*sizeof(char*));
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';
elsecd[--start]='1';
HC[i]=(char*)malloc((n-start)*sizeof(char));//为第i个字符编码分配空间
strcpy(HC[i],&cd[start]);//从cd复制编码串到HC
}
free(cd);//释放工作空间
}
voidSC_HuffmanCoding(chara[],charb[],intm,intn,HuffmanCodeHC)//输出huffman编码
{
inti,j;
printf("输入的字符串的Huffman编码:
\n");
for(i=0;ifor(j=0;jif(a[i]==b[j])
cout<printf("\n");
}
voidHuffmanDecoding(intn,charb[],HuffmanTreeHT)//对编码的二进制串进行解码
{
intm=2*n-1;
inti=m;
charc;
printf("\n请输入一串编码:
\n");
cin>>c;
printf("\n所得译码为:
\n");
while((c=='0')||(c=='1'))//判断是否是合法字符
{
if(c=='0')i=HT[i].lchild;//继续向左查找
elsei=HT[i].rchild;//继续向右查找
if(HT[i].lchild==0)//直到分支的末端
{
printf("%c",b[i-1]);//输出对应的符号
i=m;//重新开始查找下一个
}
cin>>c;
}
printf("\n");
}
voidmain()
{
HuffmanTreeHT;
HuffmanCodeHC;
chara[100],b[100];
inti=0,j=0,m=0,n=0,*w;
printf("请任意输入一组字符:
\n");
cin>>a;
while(a[i]) {m++;i++;}//用m来记录a中字符串个数
for(i=0;i{
for(j=0;j
if((iif(i}
w=(int*)malloc((n+1)*sizeof(int));
for(i=1;i<=n;i++)w[i]=0;
Calculate(a,b,m,n,w);
HuffmanCoding(HT,HC,w,n);
ASCII2(b,HC,n);
printf("\nHuffman各字符的编码如下:
\n");
printf("编码表:
\t编码频度:
\n");
for(i=1;i<=n;i++)cout<
"<printf("\n");
SC_HuffmanCoding(a,b,m,n,HC);
HuffmanDecoding(n,b,HT);
}