1. 问题
- 给定字符集 C = < x 1 , x 2 , . . . , x n > C=<x_1,x_2,...,x_n> C=<x1,x2,...,xn> 和每个字符的频率 f ( x i ) f(x_i) f(xi),求关于 C C C 的一个最优前缀码。
2. 解析
-
构造最优前缀码的贪心算法就是哈夫曼算法
-
p o p : pop: pop: 每次从队列中取出两个权值最小的节点当作孩子节点
-
p u s h : push: push: 根据 p o p pop pop 操作取出的两个子结点,构成一个带有新的权值(两个子结点权值相加)的父结点后重新放入队列。
-
重复 n − 1 n-1 n−1 次 p o p pop pop 和 p u s h push push 操作后,就构成了一颗哈夫曼树。
-
这里设定右节点的边值为1,左节点的边值为0,那么从祖先节点到某个叶子节点的边集集合就是其最优前缀编码。
3. 设计
最优前缀编码问题(哈夫曼算法)
#include<bits/stdc++.h>
const int N = 1e3+10;
using namespace std;
struct node {
int val,id;
bool friend operator<(const node &x,const node &y) {
//按照键值降序排序
if (x.val!=y.val) return x.val>y.val;
return x.id>y.id;
}
};
struct tree {
int l,r,val;
}t[N];
string s;
priority_queue<node>q;
int n,m,k;
int fa[N];
string HafCode[N];
/**
* 从叶子节点开始跳到祖先节点
*/
void getHafCode() {
for (int i=1;i<=n;i++) {
string c;
int j = i;
while (fa[j]) {
c = (j==t[fa[j]].l?'0':'1')+c;
j = fa[j];
}
HafCode[i] = c;
}
}
void Haf() {
/**
* 哈夫曼算法最多执行n-1次
*/
for (int i=1;i<n;i++) {
//每次从堆中取出2个值最小的节点
node a = q.top();
q.pop();
node b = q.top();
q.pop();
int id = i+n;
int val = a.val+b.val;
//建立树结构
t[id].l = a.id;
t[id].r = b.id;
t[id].val = val;
fa[a.id] = id;
fa[b.id] = id;
q.push({val,id});
}
getHafCode();
}
void run() {
cin>>s;
n = s.length();
for (int i=1;i<=n;i++) {
int val;
cin>>val;
q.push({val,i});
}
Haf();
for (int i=1;i<=n;i++) {
cout<<s[i-1]<<":";
cout<<HafCode[i]<<endl;
}
}
int main() {
run();
return 0;
}