1.问题
给定字符集C={X1,X2,……,Xn}和每个字符的频率或者说权重f(xi),求关于 C 的一个最优前缀码。
2.解析
构造最优前缀码的贪心算法就是哈夫曼算法(Huffman)
构造哈夫曼二叉树的步骤主要为:
- 为每个符号建立一个叶子节点,并加上其相应的发生频率
- 当有一个以上的节点存在时,进行下列循环:
- 把这些节点作为带权值的二叉树的根节点,左右子树为空
- 选择两棵根结点权值最小的树作为左右子树构造一棵新的二叉树,且至新的二叉树的根结点的权值为其左右子树上根结点的权值之和。
- 把权值最小的两个根节点移除
- 将新的二叉树加入队列中.
- 最后剩下的节点暨为根节点,此时二叉树已经完成。
而利用哈夫曼树求得的用于通信的二进制编码称为哈夫曼编码。树中从根到每个叶子节点都有一条路径,对路径上的各分支约定指向左子树的分支表示”0”码,指向右子树的分支表示“1”码,取每条路径上的“0”或“1”的序列作为各个叶子节点对应的字符编码,即是哈夫曼编码。
3.设计
struct hfmcode{
int weight;
int lchild;
int rchild;
int parent;
string code;
string ch;
};
void createHuffmantree(struct hfmcode *tree, int n, int m){
int m1, m2, i, loc1, loc2, k;
for(i = n; i < m; i ++){
/*初始化最小值、次小值*/
m1 = m2 = MIN_NUM;
loc1 = loc2 = -1;
/*在尚未构造二叉树的节点中查找权值最小的两棵树*/
for(k = 0; k < i; k ++){
if(tree[k].parent == -1){
if(tree[k].weight < m1){
m2 = m1;
loc2 = loc1;
m1 = tree[k].weight;
loc1 = k;
}else if(tree[k].weight < m2){
m2 = tree[k].weight;
loc2 = k;
}
}
}
/*修改2个小权重节点的双亲*/
tree[loc1].parent = i;
tree[loc2].parent = i;
/*修改parent的权重*/
tree[i].weight = tree[loc1].weight + tree[loc2].weight;
/*修改parent的孩子*/
tree[i].lchild = loc1;
tree[i].rchild = loc2;
}
}
void Huff_Code(struct hfmcode *tree, int n)
{
int i, j, k;
string s = "";
for (i = 0; i < n; i++){
s = "";
j = i;
while (tree[j].parent != -1){ //遍历哈夫曼树
k = tree[j].parent;
if (j == tree[k].lchild) //左子树为0,右子树为1
s = s + "0";
else
s = s + "1";
j = tree[j].parent;
}
cout << "字符 " << tree[i].ch << " 的编码为:";
for (int l = s.size() - 1; l >= 0; l--){
cout << s[l];
tree[i].code += s[l]; //保存编码
}
cout << endl;
}
}
结果:
4.分析
5.源码
https://github.com/Chenzh0205/Algorithm/tree/main/%E4%BD%9C%E4%B8%9A11