贪心-最优前缀码

二元前缀码:任何字符的代码不能作为其它字符代码的前缀.eg.Q={001,00,010,01}不是二元前缀代码,如序列0100001会产生歧义

C={x1,x2,…,xn}是n个字符的集合,f(xi)为xi出现的频率,d(xi)为xi的码长,i=1,2,…,n.

存储一个字符的平均二进制位数(码数):

  $B = \sum\limits_{i=1}^{n}f(x_i)d(x_i)$

每个二元前缀码对应一棵二叉树,树叶代表码字,树叶的深度表示码长,平均二进制位数相当于这棵树在给定频率下的平均深度,也称为这棵树的权

对同一组频率可以构造出不同的二叉树,对应的平均二进制位数也不同。占用位数越少的压缩效率越高,即每个码字平均使用二进制位数最少的前缀码,称为最优二元前缀码

如果叶片数n=2k,且每个码字的频率是1/n,那么这棵树应是一颗均衡的二叉树

问题:对于任意给定的n个频率f(x1),f(x2),…,f(xn),如何构造一棵对应于最优二元前缀码的二叉树?

Huffman算法:

输入:C={x1,x2,…,xn}字符集,每个字符的频率f(xi),i=1,2,…,n.

输出:Q

1.n<-|C|

2.Q<-C         //按频率递增构成队列 Q

3.for i<-1 to n-1 do

4.    z<-Allocate-Node()

5.    z.left<-Q中最小元      //取出Q中最小元作为z的左儿子

6.    z.right<-Q中最小元   //取出Q中最小元作为z的右儿子

7.    f(z)<-f(x)+f(y)

8.    Insert(Q,z)

9.return Q

版本一:(仅求带权路径长)

#include <iostream>
#include <queue>
using namespace std;
int main(){
    int n, c[100];
    int x, y, ans;
    priority_queue<int, vector<int>, greater<int> > Q;    // "> >"必须分开写,否则会被误认为右移运算符 
    while(cin >> n){
        while (!Q.empty()){
            Q.pop();
        }
        for (int i = 0; i < n; ++i){
            cin >> c[i];
            Q.push(c[i]);
        }
        ans = 0;
        for (int i = 1; i < n; ++i){    //进行n-1轮循环 
            x = Q.top();
            Q.pop();
            y = Q.top();
            Q.pop();
            Q.push(x + y);
            ans += (x + y);
        }
        cout << ans << endl;    //带权路径长度(WPL) 
    }
    return 0;
}

版本二:(构造前缀树)

#include <iostream>
#include <queue>
#include <deque>
using namespace std;
struct Node{    //typedef struct{}Node;和struct Node{};的区别在于,后者内部可定义Node变量 
    int freq;
    Node *left;
    Node *right;
    Node():freq(0), left(NULL), right(NULL){}
    /*friend bool operator < (Node a, Node b){    
        return a.freq > b.freq;        这段有问题,暂时未改
    } */
};
class Cmp{
public:
    bool operator() (const Node *a, const Node *b) const{
        return a->freq > b->freq;
    }
};

priority_queue<Node *, vector<Node *>, Cmp> Q;

//priority_queue<Node *> Q;
int n;
void Buildtree(){
    for (int i = 1; i < n; ++i){
        Node *x = Q.top();
        Q.pop();
        Node *y = Q.top();
        Q.pop();
        //cout << x->freq << ' ' << y->freq << endl;
        Node *tmp = new Node;
        tmp->freq = x->freq + y->freq;
        tmp->left = x;
        tmp->right = y; 
        Q.push(tmp);    //队列push是值复制,so,队列元素采取指针 
    }
}
void deletetree(Node *a){
    if (a->left)
        deletetree(a->left);
    if (a->right)
        deletetree(a->right);
    delete a;
}
void Print(Node *a){
    if (a->left)
        cout << a->left->freq << ' ';
    if (a->right)
        cout << a->right->freq << ' ';
    if (a->left)
        Print(a->left);
    if (a->right)
        Print(a->right);
}
deque<int> E;
void dfs(Node *a){    //遍历树,并输出二进制码 
    if (!a->left && !a->right){
        deque<int> F = E;
        while (!F.empty()){
            cout << F[0];
            F.pop_front();
        }
        cout << endl;
        return;
    }
    if (a->left){
        E.push_back(0);
        dfs(a->left);
        E.pop_back();
    }
    if (a->right){
        E.push_back(1);
        dfs(a->right);
        E.pop_back();
    }    
}
int main(){
    int f;
    cin >> n;
    for (int i = 0; i < n; ++i){
        cin >> f;
        Node *c = new Node;
        c->freq = f;
        Q.push(c);
        //delete c;不能delete,队列中的元素指针和这里的临时指针同时指向Node 
    }
    Buildtree();
    Node *root = Q.top();
    Q.pop();
    cout << root->freq << endl;
    //dfs(root);
    //Print(root);
    deletetree(root);
    return 0;
}

个人敲代码时遇到的一些小问题:

1)优先队列中的元素是值传递

例:

int a[] = {5, 4, 3, 2, 1, 10, 9, 8, 7, 6};
priority_queue<int> Q;
for (int i = 0; i < 10; ++i)
    Q.push(a[i]);
for (int i = 0; i < 10; ++i){
    a[i] = Q.top();
    Q.pop();
    cout << a[i] << ' ';
}

输出结果为:10 9 8 7 6 5 4 3 2 1

因此优先队列中的元素采用指针

2)循环中使用new定义/赋值指针,都会重新申请内存(原本的依然存在),重定向指针

3)优先队列两种使用方法

方法一如之前代码所示

方法二如下(我在“版本二”的注释中试图重现,但出了点小错误,有机会改吧……)

struct Node{
    int x;
    int y;
    friend bool operator < (Node a, Node b)
    {
        return a.x > b.x;    //结构体中,x小的优先级高
    }
};
priority_queue<Node> q;   //定义方法

4)friend bool operator < (Node *a, Node *b)会报错,重载运算符不能对指针单独操作,因为系统已经定义了有关指针的运算,不能改变

5)老毛病,每行代码的空格部分不能有中文空格和标点,否则会stray '\241' in program

 

转载于:https://www.cnblogs.com/victorique-de-blois/p/10639850.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值