Huffman树/最优二叉树:
(一)Huffman树的定义:
(1)节点的带权路径长度WPL(weighted path length):
根结点到该节点的路径长度与该节点的权值的乘积为节点的WPL:
(2)树的带权路径长度WPL:
树的WPL:树上所有叶子节点的带权路径长度之和。
(3)Huffman树/最优二叉树:
带权路径长度最小的二叉树为Huffman树/最优二叉树.
(二)Huffman树的特点:
(1)Huffman树上没有度数为1的节点。即n1=0
(2)n=2*n0-1;(n为节点数目,n0为叶子节点数目)
因为n=n0+n1+n2=n0+n2=n0+n0-1=2*n0-1
(三)Huffman树的构造:
构造一个有n0个叶子节点的Huffman树。
(1)思想:
1.根据给定的n个权值{w1,w2,...wn},对应节点构成n棵二叉树的森林F={T1,T2,...Tn},每棵二叉树只有一个权值为wi的根节点,左右孩子为空。
2.在F中选择两棵根节点的权值最小的二叉树,作为左右子树构造一棵新的二叉树,新的二叉树的根节点的权值为左右子树的根节点的权值之和。
3.在F中删除这两棵二叉树,将新的二叉树添加到F中。
4.重复2和3,直至F中只有一棵树,即为Huffman树。
(2)范例:
如下图:
(3)代码实现:
/*
Huffman树中的每个节点的类型:
*/
typedef struct HTNode {
DataType data;
WeightType weight;
int parent ;
int lchild;
int rchild;
};
/*
Huffman树的数据类型:
*/
typedef struct HFTree{
HTNode elem[Maxsize];//
int leafCount;//叶子节点的个数
int root; //最终的huffman树的根节点的下标。root=2*n-2
};
/*
目的:根据给出的数据序列dataArray[n],以及权值序列weightArray[n],构造一棵huffman树。
n为叶子节点个数,即序列长度。
注 : ht为引用型参数;
注意:此中,huffman树是用数组来存储各个节点的,但是不是用数组的下标来标识各个节点之间的关系。
而是通过节点中的lchild,rchild,parent(int 类型)来标识各个节点之间的关系。
*/
void createHFTree(HFTree & ht , DataType [] dataArray , WeightType [] weightArray ,int n){
WeightType minWeight1,minWeight2;//两个根节点权值最小的权值
int index_minWeight1,index_minWeight2;//根节点权值最小的节点的下标
int i ,j ,k;
for(i=0;i<n;i++){//初始化将每个叶子节点的data,weight填写
ht.elem[i].data=dataArray[i];
ht.elem[i].weight=weightArray[i];
}
for(i=0;i<2*n-1;i++){//初始化所有节点的parent,lchild ,rchild;
ht.elem[i].parent=ht.elem[i].lchild=ht.elem[i].rchild=-1;
}
/*elem[]数组的0~n-1填充的是叶子节点,n~2*n-2填充的双分支节点*/
for(i=n;i<2*n-1;i++){
minWeight1=minWeight2=MaxValue;
index_minWeight1=index_minWeight2=0;//初始化最小的权值,及其下标。
for(k=0;k<i;k++){//for循环找出根节点权值最小的两个权值,及其根节点下标
if(ht.elem[k].parent==-1){//是否为根节点
if(ht.elem[k].weight<minWeight1){//节点k的权值比最小都小
minWeight2=minWeight1;//minWeight1为最小的权值,
minWeight1=ht.elem[k].weight;//minWeight2为权值次小的权值
index_minWeight2=index_minWeight1;
index_minWeight1=k;
}
else if(ht.elem[k].weight<minWeight2){
//节点k的权值比最小大,但是比次小小。
minWeight2=ht.elem[k].weight;
index_minWeight2=k;
}
}
}
ht.elem[index_minWeight1].parent=i;
ht.elem[index_minWeight2].parent=i;//删除两个子树
ht.elem[i].lchild=index_minWeight1;//加入新的节点
ht.elem[i].rchild=index_minWeight2;
ht.elem[i].weight=minWeight1+minWeight2;
}
ht.leafCount=n;
ht.root=2*n-2;
}
(四)Huffman编码:
/*
编码类型;
注:code[start~N-1]存放一个叶子的huffman编码;
*/
#define N 10
typedef struct HFCode{
char code[N];
int start;
};
/*
目的:生成huffman编码;
思想:
1.对于当前的叶子节点ht.elem[i](0<=i<=leafCount),现将其对应的编码tempCode.start=N-1;
2.寻找当前节点的父节点,ht.elem[parent],如果当前节点为父节点的左孩子,则tempCode[start--]=0,为右孩子,则tempCode[start--]=1;
3.父节点成为新的当前节点,重复1,2,直至父节点为-1.
*/
//hfCode的每一个元素为一个叶子节点的编码
void createHFCode(HFTree & ht ,HFCode hfCodeArray[]){
int current,father;//current为当前节点的下标,father为其父节点的下标
HFCode tempCode;
int i;
for(i=0;i<ht.leafCount;i++){//为每一个叶子节点建立编码
current=i;//叶子节点存在ht的elem[0~leafCount-1]中。
father=ht.elem[i].parent;
tempCode.start=N-1;
while(father!=-1){
if(ht.elem[father].lchild==current)
tempCode.code[start--]='0';
else tempCode.code[start--]='1';
current=father;//更新current,father,向上走一步。
father=ht.elem[current].parent;
}
tempCode.start++;
htCodeArray[i]=tempCode;
}
}
------
注:也可以使用最小堆来构建Huffman树。