1、 哈夫曼编码的基本介绍3、总结一、基本术语1.什么是哈夫曼树 给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman tree)。例如:有4个结点,权值分别为7,5,2,4,构造有4个叶子结点的二叉树WPL=7*1+5*2+2*3+4*3=35 带权路径WPL值最小。所以此二叉树为哈夫曼树。哈夫曼树的特点:1.权值越小的叶子节点越远离根节点,反之,则越靠近根节点;2. 只有度为0(叶子结点)和度为2(分支结点)的结点,不存在度为1的结点。路径和路径长度在一棵树中,从一个结点往下可以达到的孩子或子孙结点之间的通路,称为路径
2、。通路中分支的数目称为路径长度。若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1。结点的权及带权路径长度若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积。树的带权路径长度树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL。2.什么是哈夫曼编码哈夫曼编码:对一棵具有n个叶子的哈夫曼树,若对树中的每个左分支赋予0,右分支赋予1,则从根到每个叶子的通路上,各分支的赋值分别构成一个二进制串,该二进制串就称为哈夫曼编码。例 要传输的字符集 D=A,B,C,D 字符出现频率 w=4,3,2
3、,1根据哈弗曼树的构造特点权值越大越靠近根节点的原则,得到下面这哈弗曼树。二、基本操作1.哈弗曼树构造的基本步骤W=5,3,2,4哈弗曼树的基本构造过程第一步:初始化5,3,2,4第二步:选取与合并 从当中选取两个数一个数字最小另外一个较小2,3。第三步:删除与加入 5,4, 重复二三步就构成了最优二叉树。哈夫曼树的类型定义:哈夫曼树是一种二叉树 ,由于哈夫曼树中没有度为1的结点,则一棵有n个叶子的哈夫曼树共有2n1个结点,可以用一个大小为2n1 的一维数组存放哈夫曼树的各个结点。由于每个结点同时还包含其双亲信息和孩子结点的信息,所以构成一个静态三叉链表。静态三叉链表中:每个结点的结构为 :各
4、结点存储在一维数组中,0号单元不使用,从1号位置开始使用。typedef struct HFTreeNode int weight ; /* 结点的权值*/ int parent ; /* 双亲的下标*/ int lchild ; /* 左孩子结点的下标*/ int rchild ; /* 右孩子结点的下标*/ HFTreeNode, HuffmanTree;2.构造哈弗曼树代码:#define MaxSize 100 /叶子数目#include HFTreeNode,HuffmanTree;void Select(HuffmanTree *HT,int g,int s);void print
5、(HuffmanTree *HT,int m);void CreatHuffmanTree(HuffmanTree TMaxSize,int n) int i,p2,lc,rc; if(n 1) printf(结点大小不能小于1); return ; int m = 2 * n;/计算哈夫曼树的结点大小 for(i = 1 ; i m ; i+) /从第1个结点开始用起 Ti.parent = 0; Ti.lchild = 0; Ti.rchild = 0; Ti.weight = 0; print(T,m);= n ; i+)请输入第%d个结点的权值:,i); scanf(%d,&Ti.we
6、ight); /* T1.weight = 5; T2.weight = 7; T3.weight = 3; T4.weight = 2; T5.weight = 8; */ for(i = n + 1 ; Select(T,i-1,p); /在T1.i - 1中选择两个权值最小的根结点,其 /序号分别为p0,p1 lc = p0; rc = p1; Tlc.parent = i;/lc为最小权的根结点即左孩子下标 Trc.parent = i;/rc为次小权的根结点即右孩子下标 Ti.lchild = lc; Ti.rchild = rc; Ti.weight = Tlc.weight +
7、Trc.weight;void Select(HuffmanTree *HT,int g,int s) int j, k, m, n,min; for(k = 1 ; k = g ; k+) if(HTk.parent = 0) s0 = k; min = HTk.weight; break; for(j = 1 ; j j+) if(HTj.weight = min & HTj.parent = 0) min = HTj.weight; s0 = j; if(HTk.parent = 0 & k != s0) s1 = k; HTj.parent = 0 & j ! s1 = j;void
8、print(HuffmanTree *HT,int m) int i; printf(下标tweighttparenttlchildtrchildn%dt%dt%dt%dt%dn,i,HTi.weight,HTi.parent,HTi.lchild,HTi.rchild);void main() int n; HuffmanTree TMaxSize;请输入叶子结点个数: scanf(n); CreatHuffmanTree(T,n);程序运行结果3.哈夫曼编码的基本介绍 在数据通信中,需要将传送的文字转换成二进制的字符串,用0,1码的不同排列来表示字符。例如,需传送的报文为“AFTER DA
9、TA EAR ARE ART AREA”,这里用到的字符集为“A,E,R,T,F,D”,各字母出现的次数为8,4,5,3,1,1。现要求为这些字母设计编码。要区别6个字母,最简单的二进制编码方式是等长编码,固定采用3位二进制,可分别用000、001、010、011、100、101对“A,E,R,T,F,D”进行编码发送,当对方接收报文时再按照三位一分进行译码。显然编码的长度取决报文中不同字符的个数。若报文中可能出现26个不同字符,则固定编码长度为5。然而,传送报文时总是希望总长度尽可能短。在实际应用中,各个字符的出现频度或使用次数是不相同的,如A、B、C的使用频率远远高于X、Y、Z,自然会想到
10、设计编码时,让使用频率高的用短码,使用频率低的用长码,以优化整个报文编码。为使不等长编码为前缀编码(即要求一个字符的编码不能是另一个字符编码的前缀),可用字符集中的每个字符作为叶子结点生成一棵编码二叉树,为了获得传送报文的最短长度,可将每个字符的出现频率作为字符结点的权值赋予该结点上,显然字使用频率越小权值越小,权值越小叶子就越靠下,于是频率小编码长,频率高编码短,这样就保证了此树的最小带权路径长度效果上就是传送报文的最短长度。因此,求传送报文的最短长度问题转化为求由字符集中的所有字符作为叶子结点,由字符出现频率作为其权值所产生的哈夫曼树的问题。利用哈夫曼树来设计二进制的前缀编码,既满足前缀编
11、码的条件,又保证报文编码总长最短。哈夫曼静态编码:它对需要编码的数据进行两遍扫描:第一遍统计原数据中各字符出现的频率,利用得到的频率值创建哈夫曼树,并必须把树的信息保存起来,即把字符0-255(28=256)的频率值以2-4BYTES的长度顺序存储起来,(用4Bytes的长度存储频率值,频率值的表示范围为0-232-1,这已足够表示大文件中字符出现的频率了)以便解压时创建同样的哈夫曼树进行解压;第二遍则根据第一遍扫描得到的哈夫曼树进行编码,并把编码后得到的码字存储起来。哈夫曼动态编码:动态哈夫曼编码使用一棵动态变化的哈夫曼树,对第t+1个字符的编码是根据原始数据中前t个字符得到的哈夫曼树来进行
12、的,编码和解码使用相同的初始哈夫曼树,每处理完一个字符,编码和解码使用相同的方法修改哈夫曼树,所以没有必要为解码而保存哈夫曼树的信息。编码和解码一个字符所需的时间与该字符的编码长度成正比,所以动态哈夫曼编码可实时进行。3哈夫曼译码 在通信中,若将字符用哈夫曼编码形式发送出去,对方接收到编码后,将编码还原成字符的过程,称为哈夫曼译码。三、总结 通过对本课程的学习,不仅对大一所学的C语言有了更加深的理解,还学习了一些新的内容数据结构。当然数据结构中包含的东西不仅仅只有哈夫曼树这一块内容,还有很多很多的内容,比如图、线性表等等。在本次哈弗曼树的课程设计中,对老师上课所提及到的内容加深了印象,同时也加入一些未涉猎的方面。学习程序这方面的东西,天赋不是很重要,最重要的是细心耐心。
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1