哈弗曼树与哈夫曼编码
哈弗曼树
一、概念
-
WPL:设二叉树有n个叶子结点,每个叶子结点带有权值Wk,Lk是根结点到该叶子结点的长度,则叶结点带权路径长度之就位WPL。
-
WPL最小的树即为最优二叉树或者哈弗曼树
-
这里有五个叶结点,权值为1、2、3、4、5,WPL最小的即为哈弗曼树,如右三
二、哈弗曼树类
```C++
//哈弗曼树结点类
template <class T> class HuffmanTreeNode{
T weight;
HuffmanTreeNode<T>* left;
HuffmanTreeNode<T>* right;
HuffmanTreeNode<T>* parent;
HuffmanTreeNode{
left=NULL;
right=NULL;
}
~HuffmanTreeNode(){}
}
//哈夫曼树类
template <class T> class HuffmanTree {
private:
HuffmanTreeNode<T>* root;//Huffman树的树根
void MergeTree(HuffmanTreeNode<T> &ht1,
HuffmanTreeNode<T> &ht2, HuffmanTreeNode<T>* parent);//把ht1和ht2两个树叶合成一个parent几点的两个儿子
public:
//构造Huffman树,weight是存储权值的数组,n是数组长度
HuffmanTree(T weight[],int n);
virtual ~HuffmanTree(){DeleteTree(root);}; //析构函数
}
三、哈弗曼树的构造
- 按权值(如频率或者权重)将字符排成一列,可以理解为将权值排成一个最小堆
- 拿走最小堆中权值最小的两个字符
- 将这两个字符即为哈弗曼树的树叶(如上图右三中1和2),将这两个树叶标位一个结点parent的两个儿子,结点parent的权值即为两个儿子权值的和。
- 将parent结点放回最小堆,再从中取出两个权值最小的字符,重复上述步骤,直到只有最后一个字符
```C++
template<class T>
HuffmanTree<T>::HuffmanTree(T weight[],int n){
MinHeap<HuffmanTreeNode<T>> heap;
HuffmanTreeNode<T>* parent,&leftchild,&rightchild;
HUffmanTreeNode<T>* NodeList=new HuffmanTreeNode<T>[n];//理解为建立一个数组(哈弗曼树结点类型),该数组元素p存放每个权值和左右儿子和父代,以便向堆中插入p
for(int i=0;i<n;i++){
NodeList[i].element=weight[i];
NodeList[i].parent=NULL;
NodeList[i].left=NodeList[i].right=NULL;
heap.Insert(NodeList[i]);//将NodeList类型数据入堆heap中,见前面堆的成员函数Insert()
}//所有元素都按照权值放在堆中
for(i=0;i<=n-1;i++){//n-1次即可,最后一次不用执行循环
firstmin=heap.RemoveMin();//从堆中找到最小值并返回
secondmin=heap.RemoveMin();
parent=new HuffmanTreeNode<T>;//给parent变量new一个空间
MergeTree(firstmin,secondmin,parent);//将两个树叶合并放到parent结点中
heap.Insert(*parent);
root=parent;//最后一次执行这个的时候,parent结点就是这个哈弗曼树的根结点
}
delete []NodeList;//释放这个当初为了往heap中存取元素的数组(哈夫曼结点类型)
}
哈弗曼编码
-
适用于字符频率不等,差别较大的情况
-
将频率视为权值进行哈弗曼树的构造。哈弗曼树的叶结点的权值与字符一一对应,字符的编码即为根结点到此叶结点的路径,走左路为0,走右路为1.
-
避免了编码的二义性,即可以无二义的解码
例如有出现频率不等的13个字符,d0-d12,则按权值对其进行哈弗曼树的构造,
然后叶结点的权值与原来的字符一一对应,得到其字符编码为
即将使用频率较高(权值高)的字符用短码表示,如d12,使用频率低(权值低)的用长码表示,如d0.
上述译码为d12d2
率较高(权值高)的字符用短码表示,如d12,使用频率低(权值低)的用长码表示,如d0.
上述译码为d12d2