一、基本概念
- 结点的路径长度:从根结点到该结点的路径上分支的数目
- 树的路径长度:树中每个结点的路径长度之和
- 结点的带权路径长度:从根结点到该结点的路径长度(lk)与结点上权(wk)的乘积。
- 树的带权路径长度:树中所有叶子结点的带权路径长度之和,WPL(T) = Σwk·lk (对所有叶子结点)。
二、赫夫曼树(最优二叉树)
1.定义
在所有含 n 个叶子结点、并且叶子结点带相同权值的二叉树中,其带权路径长度WPL最小的那棵二叉树称为最优二叉树。
2.构造最优二叉树——赫夫曼算法
采用贪心策略。组成树的每个结点作为一棵树,从中选取权值为最小和次小的两个,分别作为左、右子树构造一棵新的二叉树,并置这棵新的二叉树根结点的权值为其左、右子树根结点的权值之和;从所有树中中删去这两棵树,同时加入刚生成的新树。重复选取根结点权值最小的两个,直至 F 中只含一棵树为止(重复 n-1次)。
- n个叶子结点的赫夫曼树共有2n-1个结点
- 赫夫曼树中没有度为1的结点,结点总数为n0+n2
3.赫夫曼编码
- 前缀编码:任何一个字符的编码都不是同一字符集中另一个字符的编码的前缀。
- 利用赫夫曼树可以构造一种不等长的二进制编码,并且构造所得的赫夫曼编码是一种最优前缀编码,即使所传电文的总长度最短
- 码文长度=Σ(每个字符编码长度*lk出现频次wk)
typedef struct {
int weight;//权值
int parent, lchild, rchild;//三个指针
}HTNode, *HuffmanCode;//哈夫曼树
Typedef char **HuffmanCode;//哈夫曼码
void huffmancodeing(HuffmanTree &HT, HuffmanCode &HC, int *w, int n) {
if(n < 1) return;//n为给定的权值的数量,所以结点总数为2n-1
HT = (Huffman Tree)malloc((m+1)*sizeof(HTNode));//0号单元未用
for(p = HT + 1, i = 1; i <= n; ++p, ++w) *p = {*w, 0, 0, 0};
for(; i <= m; ++i, ++p) *p = {0,0,0,0};
for(i = n + 1 ;i <= m; ++i) {//生共成m-n个新树
Select(HT, i-1, s1, s2);//从n个叶结点里选两个权值最小的
HT[s1].parent = i;//i作为每个新树的根节点的序号
HT[s2].parent = i;
HT[i].lchild = s1;//最小-左孩子
HT[i].rchild = s2;//次小-右孩子
HT[i].weight = HT[s1].weight + HT[s2].weight;//根节点的权值为两个孩子的和
}
//从叶子到根逆向求每个字符的编码
HC = (HuffmanCode)malloc((n+1)*sizeof(char *));
cd = (char *)malloc(n*sizeof(char));
cd[n-1] = "\0";//编码结束符
int start;
for(i=1; i<=n; ++i) {
start = n-1;//编码结束符位置
for(c=i, f=HT[i].parent; f!=0; c=f, f=HT[f].parent) {
if(HT[f].lchild == c)
cd[--start] = "0";
else cd[--start] = "1";
HC[i] = (char *)malloc((n-start)*sizeof(char));
strcpy(HC[i], &cd[start]);
}
free(cd);
}
}