哈夫曼树(最优二叉树)
提前复习数据结构课的知识
哈夫曼树的建立有四个步骤,总结成口诀就是(来源:王卓老师的数据结构视频):
- 建立森林全是根
- 选择两小新树
- 删除两小添新人
- 重复 2、3 剩单根
简单的解释:
- 首先输入 N 个值作为根建立的森林
- 在森林中选择两个权值最小的根(注意这里只是选根,有没有子树无所谓,到时候会合并)
- 删除这两个根(通常是把 parent 标记为非 0 值以在第 2 步中标记删除),将他们作为左右子树且将他们的权值的和作为新树的根,加入到森林
- 重复 2、3 步直到森林里只有一个树
思想
根据权值分配叶子节点的位置,权值越大/小离根的位置也就越近,否则就越远。
简单实现
这里选两个最小数用到了 STL 里的优先队列,C 实现可以参考我之前的文章。
没有实现解码,编码功能只是使用递归简单地输出结果。
// 最后完全忘了的是输出编码...
// 输出编码的思路是:
// 从叶节点开始往上找父节点,直到到到达根,然后判断是根的左右分支来输出编码,是一个递归的过程
// 这里规定左小右大
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
struct Tree {
double weight;
int parent;
int lch, rch;
};
void huffman_code(Tree *tree, int cur, int parent);
/*
问题:
1. 定义 priority_queue 时单词拼写忘了
2. 对 priority_queue 的元素比较还不熟
3. 对优先队列的弹出元素大小判断错误(原先是 size() > 2)
4. 用 top() 时代码写成了这样:
fir = pq.top().first;
sec = pq.top().second;
pq.pop();
pq.pop();
导致结果出问题。这里 frist 是权,second 是下标,实际只会用到 second,而且用了一次 top 后如果
还想找第二小的元素要先 pop 出队最小元素
5. 最后忘了把新树 push 到队列中
*/
void create_tree(Tree *tree, int size){
priority_queue<pair<double, int>, vector<pair<double, int> >, greater<pair<double, int> > > pq;
for(int i=1; i<2*size; i++){
tree[i].lch = tree[i].rch = 0;
tree[i].parent = 0;
tree[i].weight = -1;
}
for(int i=1; i<size+1; i++){
scanf("%lf", &tree[i].weight);
pq.push(make_pair(tree[i].weight, i));
}
for(int i=size+1; i<2*size; i++){
int fir, sec;
if(pq.size() >= 2){
fir = pq.top().second;
pq.pop();
sec = pq.top().second;
pq.pop();
tree[i].lch = fir;
tree[i].rch = sec;
tree[i].weight = tree[fir].weight + tree[sec].weight;
tree[fir].parent = tree[sec].parent = i;
pq.push(make_pair(tree[i].weight, i));
}
}
for(int i=1; i<size+1; i++){
huffman_code(tree, i, tree[i].parent);
puts("");
}
/*
printf("lch: %d rch: %d parent: %d weight: %f\n",
tree[i].lch,
tree[i].rch,
tree[i].parent,
tree[i].weight);
*/
}
void huffman_code(Tree *tree, int cur, int parent){
if(parent){
huffman_code(tree, parent, tree[parent].parent);
printf("%d", tree[parent].rch==cur);
}
}
int main(){
int size = 3;
Tree tree[2*3];
create_tree(tree, size);
return 0;
}