算法报告三 哈夫曼编码

一、题目大意

给定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深搜构造答案即可。

添加新评论