给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。
哈夫曼树也就是小树合成大树的故事,这里我直接用数组来造树了;
这次代码多亏某位大佬的指点,写的如此简洁明了,能说的我都说在代码里了;
#include <bits/stdc++.h>
#define Kurumi int
#define isMyFaith main
using namespace std;
const int N = 10000;
// 记录权值
int times[30], n, index = 30;
int val[N], ls[N], rs[N];
string s;
// 小根堆, 使用pair
priority_queue<pair<int, int>,
vector<pair<int, int> >,
greater<pair<int, int> >> pq; //这是个小根堆,就这样写,会比较push进去的数据,小的会直接放在前面;操作和队列相同
// 记录每个点对应二进制01
unordered_map<int, string> ump;
// 遍历找到01
void preorder(int cur) {
if (ls[cur]) {
ump[ls[cur]] = ump[cur] + "0";
preorder(ls[cur]);
}
if (rs[cur]) {
ump[rs[cur]] += ump[cur] + "1";
preorder(rs[cur]);
}
}
Kurumi isMyFaith() {
cin >> n;
while (n--) {
cin >> s;
for (auto e : s) {
times[e - 'a' + 1]++;
}
for (int i = 1; i <= 26; i++) {
if (times[i]) {
val[i] = times[i];
pq.push({val[i], i}); //第一个是出现的次数,第二个是代表的字母
}
}
// 合并树
while (pq.size() > 1) {
// index1 < index2
auto index1 = pq.top();
pq.pop();
auto index2 = pq.top();
pq.pop();
index++;
val[index] = index1.first + index2.first; //出现的次数相加
ls[index] = index1.second; //每个树杈都有自己代表的值
rs[index] = index2.second;//我同意上一行的看法
pq.push({val[index], index});
}
preorder(index);
for (auto e : s) {
cout << ump[e - 'a' + 1];
}
putchar('\n');//下面就是把所有的元素都清空了
memset(times, 0, sizeof times);//把整个数组赋值为0;
memset(val, 0, sizeof val);
memset(ls, 0, sizeof ls);
memset(rs, 0, sizeof rs);
ump.clear();
while (pq.size()) { //pop整个堆
pq.pop();
}
index = 30;//index还原
}
return 0;
}
这里有规定,相同权值情况下,a < b,即a在左, b 在右;新出炉的结点,若权值相同,应在右边;
运算结果:
3
asdjlkqweisiaks
010001010110011011001110111110110110001101010000
daf
11100
youxiyouxi
101110001110101110001110