贪心法——Huffman树

贪心法——Huffman树

问题:

给定字符集C={c1,c2,…cn}以及每个字符频率f(ci),求关于C的一个最优前缀编码(总码长最小)。

分析:

每次选择最小频率的两个子树合并为新的子树,进行n-1次操作即可。

证明略。

代码:

#include<bits/stdc++.h>

using namespace std;

struct node{
    node* leftChild;
    node* rightChild;
    char chara;
    float freq;
    node(float _freq,char _chara){
        freq = _freq;
        chara = _chara;
        leftChild = NULL;//
        rightChild = NULL;//注意野指针问题
    }
};

struct cmp{
    bool operator()(node* n1, node* n2)
    {
        return n1->freq > n2->freq;
    }
};

// 建立Huffman树,贪心的选择
// 输入:待编码字符集c[],出现概率f[],字符数量n
// 输出:创建好的Huffman树的根节点指针
node* Huffman(char c[],float f[],int n){
    // 创建优先队列
    priority_queue<node*,vector<node*>,cmp> pq;
    // 将每个字符作为叶节点放进优先队列
    for(int i = 0;i < n;i++){
        node* tmp = new node(f[i],c[i]);
        pq.push(tmp);
    }
    // 进行n-1次操作
    while(--n){// n--与--n
        // 取出f值最大的两个子树,分别作为左子树和右子树合并成一棵新的树
        node* tmp1 = pq.top();
        pq.pop();
        node* tmp2 = pq.top();
        pq.pop();
        node* root = new node(tmp1->freq+tmp2->freq,'*');
        root->leftChild = tmp1;
        root->rightChild = tmp2;
        pq.push(root);
    }
    return pq.top();
}

// 查找某个字符的编码
// 输入:Huffman树根节点root,待查找字符c,结果记录栈res(在递归中记录中间结果很头痛,即使用全局变量或者函数加一个参数也稍显麻烦)
void showCode(node* root,char c,stack<int> res){
    if(root->chara == c){
        stack<int> res1;
        cout<<c<<": ";
        while(!res.empty()){
            res1.push(res.top());
            res.pop();
        }
        while (!res1.empty()) {
            cout<<res1.top()<<" ";
            res1.pop();
        }
        return;
    }
    if(root->leftChild==NULL && root->rightChild==NULL)
        return;
    res.push(0);
    showCode(root->leftChild,c,res);
    res.pop();
    res.push(1);
    showCode(root->rightChild,c,res);
    res.pop();
}

int main(){
    int n = 5;
    char c[5] = {'b','c','d','e','f'};
    float f[5] = {13,12,16,9,5};
    stack<int> res;
    showCode(Huffman(c, f, n), 'f', res);
}

复杂度:

n-1次操作,每次向堆中新插入元素,需要O(logn)复杂度,因此共O(nlogn)

P.S. CSDN的markdown语法中c++竟然是cpp,tmd,写了快一年才发现,离谱

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值