赫夫曼树,又称最优二叉树,是带权路径最短的树。
基本概念:
路径:一个结点到另一个结点之间的分支构成两点间的路径
路径长度:分支的数目
树的路径长度:根结点到树上每一个结点的长度之和 =》完全二叉树是路径最短的树
结点的带权路径长度:结点到根节点的路径长度与结点上权的乘积。
树的带权路径长度:树中所有叶子结点的带权路径长度之和。
构造赫夫曼树的赫夫曼算法:
1.根据给点的所有权值构成n棵二叉树的集合,每棵二叉树只有带权根节点,没有左右子树
2.每次从集合中选取权值最小的两棵树组成新的二叉树,此树根结点的权值为两颗子树权值之和
3.从集合中删除这两棵子树,添加新组成的树
4.重复2,3,直到集合中只剩下一棵树
赫夫曼树的使用:
前缀编码:若要设计产度不等的编码,则任意字符编码不能是另一字符编码的前缀。
赫夫曼编码:以字符出现的频率为权值,生成的前缀编码
赫夫曼树没有度为1的结点,n个叶子结点的赫夫曼树共有2n-1个结点。
赫夫曼树的数据结构:
typedef struct{
unsigned int weight;
unsigned int parent, lchild ,rchild ;
}HTNode,*HuffmanTree;
typedef char **HuffCode;
void HuffmanCoding(HuffmanTree &HF,HuffCode &HC,int* w,int n){
//建立huffman树并编码
m = 2n -1;
HF = (HuffmanTree) malloc ( (m + 1) * sizeof(HTNode));
for(p = HF,i =1;i <= n;++p,++i,++w) *p = {*w,0,0,0};
for(;i <= m; ++p,++i,++w) *p = {0,0,0,0};
}
for(i = n+1; i <+m,++i){
Select(HT,i-1,s1,s2); //永远从前面选择权值最小且parent = 0的两棵树
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;
}
//求每一个字符的编码
HC = (HuffCode) malloc ((n+1)* sizeof(char*));
cd = (char*) malloc (m * sizeof(char));
cd[n-1] = '/0';
for(i = 1; i<= n; ++i){
start = n-1; //start 用来指示编码字符的起始位置
//c: current f :father parent
for(c =i,f = HF[i].parent;f != 0; c = f, f = HF[f].parent){
if(HF[f].lchild = c) cd[--start] = '0';
else cd[--start] = '1';
}
//n-start 指示编码的长度,即向左移了多少个字符
HC[i] = (char*) malloc ((n-start)*sizeof(char) );
strcpy(HC[i],&cd[start]); //cd[start]编码字符起始位置
}
free(cd);
}
遍历赫夫曼树求编码方法:
1.自底向上
如上,从自己出发,一层一层向上,如果它是父节点的左子树,那么编码字符为0,为右即为1,直到找到根节点,用start记录编码字符串的起始位置,n-start为其编码字符串长度
2.自顶向下
如下
//m指示根节点
p=m,cdlen =0;
for(i=1;i<=m;++i) HT[i].weight =0;//全部权值归零
while(p){
if(HT[p].weight == 0){//表示此结点还没有遍历过
HT[p].weight == 1;
if(HT[p].lchild != 0) cd[cdlen++] ='0';
else(HT[p].rchilde == 0){//左右子树都为空,登记叶子结点的编码字符串
HC[p] = (char*) malloc ((cdlen+1)*sizeof(char));
cd[cdlen] = '\0';
strcpy(HC[p],cd);
}
}
else if(HT[p].weight == 1){//遍历过一次,即左子树已经遍历完了
HT[p].weight == 2;
if(HT[p].rchild != 0){
p = HT[p].rchild; cd[cdlen++] = '1';
}
}
else{ //左右子树已经遍历完,退回
HT[p].weight == 0;
p = HT[p].parent; --cdlen;
}
}