算法报告三 哈夫曼编码
16122020 钟顺源
一、题目大意
给定n个数的出现频率,问几个数的哈夫曼编码是多少?
在构造Huffman树的过程中有些要求:
1.左儿子标记为0,右儿子标记为1;
2.左儿子的权值>=右儿子的权值;
3.相同权值w的两个字母x、y,出现时间晚的优先级高。
二、分析
那这其实就是贪心+模拟。
用什么模拟? 优先队列,也就是堆。
何为模拟?每次弹出堆中最小的两个值,合并成一个权值为两最小权值之和的新节点,并维护左右孩子,并插入堆中。
可以得知,插入堆的节点需要有这么几个参数:时间戳,频次,左孩子,右孩子。
struct node{
int id;
int lid,rid;
int val;
bool operator<(const node&q)const{
if(val==q.val) return id<q.id;
return val>q.val;
}
}a[1<<13];
那么为了构造唯一Huffman树,需要重载比较运算符:若频次一致,则插入时间晚的点优先级高,否则频次低的点优先级高。
构造出Huffman树后,再从根节点往下递归,左枝加‘0’,右枝加‘1’,遇到树叶,记忆化并返回。
最后按1~n的时间戳输出叶子节点与其编码即可。
三、代码
#include<bits/stdc++.h>
using namespace std;
#define clr(a, x) memset(a, x, sizeof(a))
#define mp(x, y) make_pair(x, y)
#define pb(x) push_back(x)
#define X first
#define Y second
#define fastin \
ios_base::sync_with_stdio(0); \
cin.tie(0);
typedef long long LL;
typedef pair<int,int> PII;
const int INF = 0x3f3f3f3f;
struct node{
int id;
int lid,rid;
int val;
bool operator<(const node&q)const{
if(val==q.val) return id<q.id;
return val>q.val;
}
}a[1<<13];
priority_queue<node> q;
string ans[1<<13];
void dfs(node s,string tmp){
if(s.lid!=-1){
ans[s.lid]=tmp+'0';
dfs(a[s.lid],ans[s.lid]);
}
if(s.rid!=-1){
ans[s.rid]=tmp+'1';
dfs(a[s.rid],ans[s.rid]);
}
}
int main()
{
int t;scanf("%d",&t);
int cas=0;
while(t--){
while(!q.empty()) q.pop();
int n;scanf("%d",&n);
for(int i=1,x;i<=n;i++){
cin>>x;
a[i] = node{i,-1,-1, x};
q.push(a[i]);
}
int id=n;
while(q.size()!=1){
node r=q.top();q.pop();
node l=q.top();q.pop();
id++;
a[id] = node{id,l.id,r.id,r.val+l.val};
q.push(a[id]);
}
node s=q.top();q.pop();
for(int i=0;i<(1<<13);i++) ans[i].clear();
dfs(s,"");
printf("Case %d\n",++cas);
for(int i=1;i<=n;i++)
cout<<a[i].val<<" "<<ans[i]<<endl;
cout<<endl;
}
return 0;
}
四、体会
这是做到现在为止最简单的实验了,只需要按照给定的规则构造堆,再合并两个权值最小的点,直到合并到一个点。最后再一个dfs深搜构造答案即可。