赫夫曼树及赫夫曼编码
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了赫夫曼树及赫夫曼编码相关的知识,希望对你有一定的参考价值。
参考技术A
在1952年美国数学家发明了赫夫曼编码,当时是为了解决远距离通信(主要是电报)的数据传输最优化问题,节约存储和传送的成本。后人为纪念他的成就,就把他在编码中用到的二叉树称为赫夫曼树,别名最优二叉树。他的编码方法称为赫夫曼编码。我们平时所用的压缩和解压缩技术也是基于赫夫曼的研究之上而来。
路径长度: 从树中一个结点到另一个结点的分支构成两个节点之间的路径,路径上的分支长度称为路径长度。比如说在下图中的根节点到D结点的路径长度为2.
树的路径长度: 树根到每一结点的路径长度之和。比如说下图中树的路径长度为:1+1+2 4+3 2=16
带权路径长度: 结点的带权路径长度为从该结点到根结点之间的路径长度与结点上权的乘积。比如说D结点的带权路径长度为60.树的带权路径长度为树中所有叶子结点的带权路径长度之和。比如说下图中整棵树的带权路径长度WPL为:220. 其中树的带权路径长度(WPL)最小的二叉树称为赫夫曼树。 既然要使得树的路径长度最小,那么权值越大的节点理应离根节点越近
知道了赫夫曼树的定义后,那么如果给定一串权值,我们如何构建一颗赫夫曼树呢? 构造赫夫曼树的算法描述:
1.根据给定的n个权值w1,w2,...,wn构成n棵二叉树(只含一个节点)的集合F= T1,T2,...,Tn.,其中每颗二叉树Ti中只有一个带权为Wi的根节点,其左右子树为空。
2.在F中选取两颗根节点的权值最小的树作为左右子树构造一颗新的二叉树,并且新的二叉树的根节点的权值为左右子树根节点上的权值之和。
3.在F中删除这两棵树,将新的到的二叉树加入F中。
4.重复二三,直到F中只剩下一棵树为止,这就是赫夫曼树。
简单实现根据权值构建赫夫曼树案例如下:
测试:
输入权值集合5,15,10,30,40结果可得到下图所示的最优二叉树:
因为赫夫曼树中给定叶子节点数是可以知道赫夫曼树节点总数的,所以选择分配一段连续的空间来存储赫夫曼树。
赫夫曼编码: 假设有一段需要编码的字符集c1,c2,c3,...,cn,求得各个字符在电报中出现的频率集合为w1,w2,w3,...,wn,以c1,c2,c3,...,cn作为叶子节点,以w1,w2,w3,...,wn作为相应叶子节点的权值来构造一颗赫夫曼树。并且规定好赫夫曼树的左分支代表0,右分支代表1(即一个结点的左孩子为0,右孩子为1),则一个字符的编码就是从根节点到叶子节点所经过的路径分支组成的0和1的序列。这就是赫夫曼编码。比如说A字符的编码为01.这样就可以得到比原来编码更短的二进制串,压缩了数据,节约了存储成本。
通俗来说,就是给你一段字符,按照何种方式或者规则编码可以使得编码后所得的二进制串最短呢?赫夫曼就提出了赫夫曼编码规则。
哈夫曼树和哈夫曼编码
哈夫曼树和哈夫曼编码:(数据结构 C/C++)
从终端输入若干个字符,统计字符出现的频率,将字符出现的频率作为节点的权值,建立哈夫曼树,然后对各字符进行哈夫曼编码,最后打印哈夫曼树和对应的哈夫曼编码
谢谢!!
哈夫曼树(霍夫曼树)又称为最优树.
1、路径和路径长度
在一棵树中,从一个结点往下可以达到的孩子或孙子结点之间的通路,称为路径。通路中分支的数目称为路径长度。若规定根结点的层数为1,则从根结点到第L层结点的路径长
哈夫曼树 (3张)
度为L-1。
2、结点的权及带权路径长度
若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积。
3、树的带权路径长度
树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL。
构造
假设有n个权值,则构造出的哈夫曼树有n个叶子结点。 n个权值分别设为 w1、w2、…、wn,则哈夫曼树的构造规则为:
(1) 将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有一个结点);
(2) 在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;
(3)从森林中删除选取的两棵树,并将新树加入森林;
(4)重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。
哈夫曼编码
在数据通信中,需要将传送的文字转换成二进制的字符串,用0,1码的不同排列来表示字符。例如,需传送的报文为“AFTER DATA 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,自然会想到设计编码时,让
哈夫曼树 (4张)
使用频率高的用短码,使用频率低的用长码,以优化整个报文编码。
为使不等长编码为前缀编码(即要求一个字符的编码不能是另一个字符编码的前缀),可用字符集中的每个字符作为叶子结点生成一棵编码二叉树,为了获得传送报文的最短长度,可将每个字符的出现频率作为字符结点的权值赋予该结点上,显然字使用频率越小权值越小,权值越小叶子就越靠下,于是频率小编码长,频率高编码短,这样就保证了此树的最小带权路径长度效果上就是传送报文的最短长度。因此,求传送报文的最短长度问题转化为求由字符集中的所有字符作为叶子结点,由字符出现频率作为其权值所产生的哈夫曼树的问题。利用哈夫曼树来设计二进制的前缀编码,既满足前缀编码的条件,又保证报文编码总长最短。
哈夫曼静态编码:它对需要编码的数据进行两遍扫描:第一遍统计原数据中各字符出现的频率,利用得到的频率值创建哈夫曼树,并必须把树的信息保存起来,即把字符0-255(2^8=256)的频率值以2-4BYTES的长度顺序存储起来,(用4Bytes的长度存储频率值,频率值的表示范围为0--2^32-1,这已足够表示大文件中字符出现的频率了)以便解压时创建同样的哈夫曼树进行解压;第二遍则根据第一遍扫描得到的哈夫曼树进行编码,并把编码后得到的码字存储起来。
哈夫曼动态编码:动态哈夫曼编码使用一棵动态变化的哈夫曼树,对第t+1个字符的编码是根据原始数据中前t个字符得到的哈夫曼树来进行的,编码和解码使用相同的初始哈夫曼树,每处理完一个字符,编码和解码使用相同的方法修改哈夫曼树,所以没有必要为解码而保存哈夫曼树的信息。编码和解码一个字符所需的时间与该字符的编码长度成正比,所以动态哈夫曼编码可实时进行。
2、哈夫曼译码
在通信中,若将字符用哈夫曼编码形式发送出去,对方接收到编码后,将编码还原成字符的过程,称为哈夫曼译码。 参考技术A http://hi.baidu.com/%B3%CC%D0%F2%D0%A1%B1%F8/blog/category/%CA%FD%BE%DD%BD%E1%B9%B9
//* * * * * * * * * * * * * * * * * * * * * * * *
//*CHAPTER :4 (4_4) *
//*PROGRAM :哈夫曼树 *
//*CONTENT :构造哈夫曼树,哈夫曼编码 *
//* * * * * * * * * * * * * * * * * * * * * * * *
#include <dos.h>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
unsigned int weight; //结点权值
unsigned int parent,lchild,rchild; //结点的父指针,左右孩子指针
HTNode,*HuffmanTree; //动态分配数组存储哈夫曼树
typedef char **HuffmanCode; //动态分配数组存储哈夫曼编码表
void CreateHuffmanTree(HuffmanTree &,unsigned int*,int ); //生成一棵哈夫曼树
void HuffmanCoding(HuffmanTree,HuffmanCode &,int ); //对哈夫曼树进行编码
void PrintHuffmanCode(HuffmanCode,unsigned int*,int); //显示哈夫曼编码
void Select(HuffmanTree,int,int&,int&); //在数组中寻找权值最小的两个结点
void main()
HuffmanTree HT; //哈夫曼树HT
HuffmanCode HC; //哈夫曼编码表HC
int n,i; //n是哈夫曼树叶子结点数
unsigned int *w; //w存放叶子结点权值
......
...
...本回答被提问者采纳
以上是关于赫夫曼树及赫夫曼编码的主要内容,如果未能解决你的问题,请参考以下文章