哈夫曼树
判断树:用于描述分类过程的二叉树
树的路径长度: 树根到每一个结点的路径长度之和。记作TL
权(weight): 将树中结点赋给一个有着某种含义的数值。
结点的带权路径长度:从根结点到该结点之间的路径长度与该结点权的乘积。
树的带权路径长度: 树中叶子结点的带权路径长度之和。
构造哈夫曼树
1、构造森林全是根;2、选用两小造新树
3、删除两小添新人;4、重复2、3剩单根
构造算法实现
typedef struct HTNode{
int weight;
int parent,lch,rch;
} HTNode, *HuffmanTree;
例:有 n=8 ,权值为W = {7,19,2,6,32,3,21,10} 构造哈夫曼树
构造算法的思路 (结构数组):
代码实现
void CreateHuffmanTree(HuffmanTree& HT,int n){
//构造哈夫曼树
if(n<=1) return;
int m=2*n-1;
HT=new HTNode[m+1]; //0单元号未用,下标从1开始
for(int i=1;i<=m;i++){ //初始化,将下标1~m号结点的双亲,左孩子,右孩子置为0
HT[i].parent=0;
HT[i].lchild=0;
HT[i].rchild=0;
}
for(int i=1;i<=n;i++){
cin >> HT[i].weight; //输入前n个结点的权值
}
// 初始化结束,下面创开始创建哈夫曼树
for(int i=n+1;i<=m;i++){
/* 在select函数中,在前n个结点中通过n-1次的选择两个权值较小的结点,
进行合并,删除 来创建哈夫曼树 */
int s1=0,s2=0;
Select(HT,i-1,&s1,&s2);
/* 在 Parent == 0 中挑选出两个权值较小的结点,且s1<s2;
合并成一个新的结点,新的结点的结点号为i ,此时s1和s2结点的双亲结点即为i */
HT[s1].parent=i;
HT[s2].parent=i;
HT[i].lchild=s1;//将s1和s2分别作为结点i的左右孩子
HT[i].rchild=s2;
HT[i].weight=HT[s1].weight+HT[s2].weight;//结点i的权值为左右孩子之和
}
}
哈夫曼编码
编码方式
- 统计字符集中每个字符在电文中出现的平均频率(概率越大,要求编码越短)
- 利用哈夫曼树的特点:权越大的叶子离根越近;将每个字符的概率值作为权值,构造哈夫曼树。则概率越大的结点,路径越短。
- 在哈夫曼树的每个分支标上0或1:结点的左分支标0,右分支标1
- 把从根到每个叶子的路径上的标号连接起来,作为该叶子代表的字符的编码
例:
设组成电文的字符集D及其分布概率W为:
D={ A , B , C , D , E , F , G }
W={ 0.40 , 0.30 , 0.15 , 0.05 , 0.04 , 0.03 , 0.03 } 设计哈夫曼编码
A 0 ; B 11; C 01; D 10011; E 10010 ; F 10001
注:
1、哈夫曼编码保证是前缀编码:因为没有一片叶子是另一片叶子的祖先,所以每个叶节点的编码就不能使其他叶结点编码的前缀。
2、因为哈夫曼树的带权路径长度最短,故字符编码的总长最短。