哈弗曼编码译码器.docx

上传人:b****3 文档编号:3809509 上传时间:2022-11-25 格式:DOCX 页数:29 大小:133.08KB
下载 相关 举报
哈弗曼编码译码器.docx_第1页
第1页 / 共29页
哈弗曼编码译码器.docx_第2页
第2页 / 共29页
哈弗曼编码译码器.docx_第3页
第3页 / 共29页
哈弗曼编码译码器.docx_第4页
第4页 / 共29页
哈弗曼编码译码器.docx_第5页
第5页 / 共29页
点击查看更多>>
下载资源
资源描述

哈弗曼编码译码器.docx

《哈弗曼编码译码器.docx》由会员分享,可在线阅读,更多相关《哈弗曼编码译码器.docx(29页珍藏版)》请在冰豆网上搜索。

哈弗曼编码译码器.docx

哈弗曼编码译码器

 

课程设计

 

哈夫曼编码/译码器

 

系别:

专业(班级):

作者(学号):

指导教师:

完成日期:

目录

1项目研究背景与意义3

1.1项目研究背景3

1.2项目研究意义3

2需求分析4

2.1设计要求与分析4

2.2基本要求4

3概要设计5

3.1系统功能模块图5

3.2主要功能函数5

4详细设计6

4.1数据结构的定义6

4.1.1宏定义6

4.1.2huffman树的结点结构6

4.1.3huffman编码结构6

4.1.4字符及其编码的结构6

4.2建立哈夫曼树7

4.2.1基本原理7

4.2.2构造思想7

4.2.3具体构造方法8

4.2.4建立huffuman树函数9

4.3建立huffman编码11

4.3.1构造思想11

4.3.2建立huffman编码函数11

4.4输出huffman编码函数12

4.5对字符串进行编码12

4.5.1构造思想12

4.5.2字符串编码函数13

4.6将权值数据存放在数据文件(文件名为data.txt中)的程序18

5调试分析19

5.1输出huffman编码,即空格和26个大写字母的界面。

19

5.2对字符串进行编码的界面19

5.3将权值数据存放在数据文件(文件名为data.txt)的界面19

6总结20

参考文献21

附录源代码22

1项目研究背景与意义

1.1项目研究背景

在科技飞速发展的今天,信息传输显得尤为重要,这主要体现在计算机之间的数据传输,主要包括文字,声音,图像,视频等文件的传输,而计算机不能直接处理这些信息,计算机只能识别二进制数据。

例如文字,在数据通讯中,传送方要将传送的文字转换成由二进制字符0、1组成的二进制串,而接受方要将二进制字符0、1组成的二进制串还原成对应的文字形式。

于是便完成了文字数据的传输过程。

时下,数据结构是一门随着计算机科学的发展而逐渐形成的新兴学科。

随着计算机技术的发展,计算机的应用领域从最初的科学计算发展到人类社会的各个领域,计算机处理的对象也由纯粹的数值发展到字符、表格、图像和声音等各种具有一定结构的数据,这就给程序设计带来一些新的问题。

与飞速发展的计算机硬件相比,计算机软件的发展相对缓慢。

研究数据结构可以解决计算机之间的数据传输效率低下的问题。

在数据结构中,研究树形结构对数据编码有很大作用,对数据构造二叉树编码应用广泛。

1.2项目研究意义

本设计旨在解决数据通讯中传输字符过长的问题,哈夫曼编码能很好地解决这个问题。

哈夫曼编码是根据哈夫曼算法够造的哈夫曼树所得到的,哈夫曼算法使得编码总长度最小。

哈夫曼编码的理论依据是变长编码理论,将使用次数多的代码转换成长度较短的代码,而是用次数少的可以使用较长的编码,并且保持编码的唯一可解性。

利用哈夫曼编码进行信息通讯可以大大提高信道利用率,缩短信息传输时间,降低传输成本。

2需求分析

2.1设计要求与分析

在当今信息爆炸时代,如何采用有效的数据压缩技术节省数据文件的存储空间和计算机网络的传送时间已越来越引起人们的重视,哈夫曼编码正是一种应用广泛且非常有效的数据压缩技术。

哈夫曼编码的应用很广泛,利用哈夫曼树求得的用于通信的二进制编码称为哈夫曼编码。

哈夫曼树中从根到每个叶子都有一条路径,对路径上的各分支约定:

指向左子树的分支表示“0”码,指向右子树的分支表示“1”码,取每条路径上的“0”或“1”的序列作为和各个叶子对应的字符的编码,这就是哈夫曼编码。

假设每种字符在字符中出现的次数为Wi,编码长度为Li,字符中有n种字符,则字符编码总长为WiLi。

若将此对应到二叉树上,Wi为叶节点的权,Li为根节点到叶节点的路径长度。

那么,WiLi恰好为二叉树上带权路径长度。

2.2基本要求

设计一个利用哈夫曼算法的编码和译码系统,重复地显示并处理以下项目,直到选择退出为止。

1将权值数据存放在数据文件(文件名为data.txt,位于执行程序的当前目录中)

2采用静态存储结构

3初始化:

键盘输入字符集大小n、n个字符和n个权值,建立哈夫曼树;

4编码:

利用建好的哈夫曼树生成哈夫曼编码;

5输出编码;

6设字符集及频度如下表:

字符空格ABCDEFGHIJKLM

频度1866413223210321154757153220

字符NOPQRSTUVWXYZ

频度5763151485180238181161

3概要设计

3.1系统功能模块图

 

图3-1系统功能模块图

3.2主要功能函数

main函数中调用了5个主要功能函数,即

1建立huffuman树函数:

voidsethfmtree()

2找权值最小的2个结点函数:

voidselect(ints);

3建立huffman编码函数:

voidsethfmcode()

4输出huffman编码函数:

voidprinthfmcode()

5对字符串进行编码函数:

voidsetcode()

其函数调用关系如图3-2所示。

图3-2函数调用关系图

4详细设计

4.1数据结构的定义

4.1.1宏定义

1定义了N为27,表示27个字符,即空格和26个大写字母

#defineN27

2定义了M为2*N-1,表示huffman树的结点个数

#defineM2*N-1

3定义了maxnumber为一个很大的数

#definemaxnumber10000

4.1.2huffman树的结点结构

structnode

{

intweight;//结点权值

intparent,lchild,rchild;//双亲,左孩子,右孩子

};

4.1.3huffman编码结构

structcodetype

{

intstart;//起始位置

charcodes[N+1];//存放哈夫曼编码的数组

};

4.1.4字符及其编码的结构

structelement

{

charcharacter;//字符

structcodetypecode;//字符编码

};

4.2建立哈夫曼树

4.2.1基本原理

哈夫曼在上世纪五十年代初提出这种编码时,根据字符出现的概率来构造平均长度最短的编码。

它是一种变长的编码。

在编码中,若各码字长度严格按照码字所对应符号出现概率的大小的逆序排列,则编码的平均长度是最小的。

(注:

码字即为符号经哈夫曼编码后得到的编码,其长度是因符号出现的概率而不同,所以说哈夫曼编码是变长的编码。

一般而言,给定n个实数w1,w2,......,wn(n≥2),求一个具有n个结点的二叉数,使其带权路径长度最小。

所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。

树的带权路径长度记为WPL=(W1*L1+W2*L2+W3*L3+...+Wn*Ln),N个权值Wi(i=1,2,...n)构成一棵有N个叶结点的二叉树,相应的叶结点的路径长度为Li(i=1,2,...n)。

可以证明哈夫曼树的WPL是最小的。

4.2.2构造思想

由哈夫曼算法的定义可知,初始森林中共有n棵只含有根节点的二叉树。

算法的第二步是:

将当前森林中的两棵根节点权值最小的二叉树,合并成一棵新的二叉树;每合并一次,森林中就减少一棵树,产生一个新结点。

显然要进行n-1次合并,所以共产生n-1个新结点,它们都是具有两个孩子的分支结点。

由此可知,最终求得的哈夫曼树中一共有2n-1个结点,其中n个叶节点是初始森林的n个孤立节点。

并且哈夫曼树中没有度数为1的分支结点。

我们可用一个大小为2n-1的一维数组来存储哈夫曼树中的结点。

要实现哈夫曼树算法,首先要实现在hfmcodes[N+1]中选择parent为0且权值最小的两个根结点的选择算法。

4.2.3具体构造方法

1首先根据给定的权值构造n棵二叉树的森林,其中每个二叉树都只有一个权值为treeweight[i]的根节点

2在森林中选取两个权值最小的根节点作为一个新二叉树的左右结点

其具体函数为:

voidselect(ints)//找权值最小的2个结点

{

inti;

intv1,v2;

v1=v2=maxnumber;

x1=x2=0;

for(i=1;i<=s;i++)

if(tree[i].parent==0)

if(tree[i].weight

{

v2=v1;

x2=x1;

v1=tree[i].weight;

x1=i;

}

elseif(tree[i].weight

{

v2=tree[i].weight;

x2=i;

}

}

通过遍历所有节点找到权值最小的结点

3设新二叉树的根节点为左右子结点的权值和其函数表达为

for(i=N+1;i<=M;i++)//找权值最小的2个结点,组成huffman树

{

select(i-1);//调用找权值最小的2个结点函数

tree[x1].parent=i;

tree[x2].parent=i;

tree[i].lchild=x1;

tree[i].rchild=x2;

tree[i].weight=tree[x1].weight+tree[x2].weight;

}

4从森林中删除选中的那两棵二叉树,同时加入新构造的那个二叉树

5重复上述步骤,得到所需的哈夫曼二叉树

4.2.4建立huffuman树函数

voidsethfmtree()//建立huffuman树

{

inti,j;

inttreeweight[N]={186,63,13,22,32,103,21,15,47,57,1,5,32,20,57,63,15,1,48,51,80,23,8,18,1,16,1};

charhfmcodesch[N]=

{'','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};

for(i=1;i<=M;i++)//初始化双亲,左右孩子结点为0

tree[i].parent=tree[i].lchild=tree[i].rchild=0;

for(i=1;i<=N+1;i++)//初始化27个字符及其权值

{

hfmcodes[i].ch=hfmcodesch[i-1];//hfmcodes[N+1]n个结点的huffman编码表

tree[i].weight=treeweight[i-1];

}

for(i=N+1;i<=M;i++)//找权值最小的2个结点,组成huffman树

{

select(i-1);//调用找权值最小的2个结点函数

tree[x1].parent=i;

tree[x2].parent=i;

tree[i].lchild=x1;

tree[i].rchild=x2;

tree[i].weight=tree[x1].weight+tree[x2].weight;

}

}

voidselect(ints)//找权值最小的2个结点

{

inti;

intv1,v2;

v1=v2=maxnumber;

x1=x2=0;

for(i=1;i<=s;i++)

if(tree[i].parent==0)

if(tree[i].weight

{

v2=v1;

x2=x1;

v1=tree[i].weight;

x1=i;

}

elseif(tree[i].weight

{

v2=tree[i].weight;

x2=i;

}

}

4.3建立huffman编码

4.3.1构造思想

给定字符集的哈夫曼树生成后,求哈夫曼编码的具体实现过程是:

依次以叶子为出发点,向上回溯至根为止。

上溯时走左分支则生成代码0,走右分支则生成代码1。

4.3.2建立huffman编码函数

voidsethfmcode()

{

inti,s,f,k=1;

structcodetypec;

for(i=1;i<=N;i++)

{

c=hfmcodes[i].code;

c.start=N+1;

s=i;

f=tree[s].parent;

do

{

c.start--;

if(s==tree[f].lchild)

c.codes[c.start]='0';

else

c.codes[c.start]='1';

s=f;

f=tree[s].parent;

}while(f);

hfmcodes[i].code=c;

}

}

4.4输出huffman编码函数

voidprinthfmcode()//输出huffman编码,即空格和26个大写字母

{

inti,j;

structcodetypec;

printf("哈夫曼的编码为:

\n");

for(i=1;i<=N;i++)

{

printf("%c:

",hfmcodes[i].character);

c=hfmcodes[i].code;

for(j=c.start;j<=N;j++)

printf("%c",c.codes[j]);

printf("\n");

}

printf("\n\n--------------------------------------------------------------------\n\n");

}

4.5对字符串进行编码

4.5.1构造思想

①由于生成的编码与要求的编码反序,将生成的代码先从后往前依次存放在一个临时向量中,并设一个指针start指示编码在该向量中的起始位置(start初始时指示向量的结束位置)。

②当某字符编码完成时,从临时向量的start处将编码复制到该字符相应的位串hfmchars中即可。

③因为字符集大小为n,故变长编码的长度不会超过n,加上一个结束符'\0',hfmchars的大小应为n+1。

4.5.2字符串编码函数

voidsetcode()//对字符串进行编码

{

inti,j,count;

charc0,k='Y';

structcodetypec;

while(k=='Y')//利用循环,实现对字符串多次编码

{

printf("\n\n请输入字符串(大写字母和空格,不多于1000个字符),输入'.'(+回车)号结束\n\n");

for(i=1;i<=100;i++)//利用循环,实现对字符串输入

{

c0=getchar();

t[i].character=c0;

if(c0=='.')break;

}

count=i;//记录字符串个数

printf("\n\n--------------------------------------------------------------------\n\n");

printf("\n\n字符串编码为:

\n\n");

for(i=1;i<=count;i++)//利用循环,实现对字符串输出

{

if(t[i].character=='.')break;

else

switch(t[i].character)//利用开关语句,查找编码

{

case'':

c=hfmcodes[1].code;

for(j=c.start;j<=N;j++)//输出编码

printf("%C",c.codes[j]);

printf("");

break;

case'A':

c=hfmcodes[2].code;

for(j=c.start;j<=N;j++)

printf("%c",c.codes[j]);

printf("");

break;

case'B':

c=hfmcodes[3].code;

for(j=c.start;j<=N;j++)

printf("%c",c.codes[j]);

printf("");

break;

case'C':

c=hfmcodes[4].code;

for(j=c.start;j<=N;j++)

printf("%c",c.codes[j]);

printf("");

break;

case'D':

c=hfmcodes[5].code;

for(j=c.start;j<=N;j++)

printf("%c",c.codes[j]);

printf("");

break;

case'E':

c=hfmcodes[6].code;

for(j=c.start;j<=N;j++)

printf("%c",c.codes[j]);

printf发

printf("%c",c.codes[j]);

printf("");

break;

case'M':

c=hfmcodes[14].code;

for(j=c.start;j<=N;j++)

printf("%c",c.codes[j]);

printf("");

break;

case'N':

c=hfmcodes[15].code;

for(j=c.start;j<=N;j++)

printf("%c",c.codes[j]);

printf("");

break;

case'O':

c=hfmcodes[16].code;

for(j=c.start;j<=N;j++)

printf("%c",c.codes[j]);

printf("");

break;

case'P':

c=hfmcodes[17].code;

for(j=c.start;j<=N;j++)

printf("%c",c.codes[j]);

printf("");

break;

case'Q':

c=hfmcodes[18].code;

for(j=c.start;j<=N;j++)

printf("%c",c.codes[j]);

printf("");

break;

case'R':

c=hfmcodes[19].code;

for(j=c.start;j<=N;j++)

printf("%c",c.codes[j]);

printf("");

break;

case'S':

c=hfmcodes[20].code;

for(j=c.start;j<=N;j++)

printf("%c",c.codes[j]);

printf("");

break;

case'T':

c=hfmcodes[21].code;

for(j=c.start;j<=N;j++)

printf("%c",c.codes[j]);

printf("");

break;

case'U':

c=hfmcodes[22].code;

for(j=c.start;j<=N;j++)

printf("%c",c.codes[j]);

printf("");

break;

case'V':

c=hfmcodes[23].code;

for(j=c.start;j<=N;j++)

printf("%c",c.codes[j]);

printf("");

break;

case'W':

c=hfmcodes[24].code;

for(j=c.start;j<=N;j++)

printf("%c",c.codes[j]);

printf("");

break;

case'X':

c=hfmcodes[25].code;

for(j=c.start;j<=N;j++)

printf("%c",c.codes[j]);

printf("");

break;

case'Y':

c=hfmcodes[26].code;

for(j=c.start;j<=N;j++)

printf("%c",c.codes[j]);

printf("");

break;

case'Z':

c=hfmcodes[27].code;

for(j=c.start;j<=N;j++)

printf("%c",c.codes[j]);

printf("");

break;

default:

printf("*此编码找不到,请检查输入是否正确*");

}

}

printf("\n\n\n--------------------------------------------------------------------\n\n");

printf("\n\n\n-----是否继续?

?

(Y/N)Y表示继续,N表示不继续-----\n");

getchar();

printf("\n");

scanf("%c",&k);

getchar();

printf("\n\n*********************************************************\n\n");

}

4.6将权值数据存放在数据文件(文件名为data.txt中)的程序

5调试分析

5.1输出huffman编码,即空格和26个大写字母的界面。

图5-1输出huffman编码

5.2对字符串进行编码的界面

图5-2字符串编码

5.3将权值数据存放在数据文件(文件名为data.txt)的界面

图5-3存放数据文件(文件名为data.txt)

6总结

参考文献

[1]严蔚敏.数据结构.C语言版.清华大学出版社.2013

[2]严蔚敏等.数据结构题集.C语言版.清华大学出版社.2013

[3]曲建民,刘元红,郑陶然.数据结构.C语言版.清华大学出版社.

2005

[4]李春葆编著.数据结构教程.清化大学出版社.2005

[5]蒋立翔编著.C++程序设计技能百练.中国铁道出版社.2004

附录源代码

#include

#include

#include

#defineN27//空格和26个大写字母

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 工程科技 > 能源化工

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1