Huffman编码(贪心--算法第四章)

Huffman编码

数据压缩过程称为编码,即将文件中的每个字符均转换为一个唯一的二进制位串。
编码方案通常有两种:等长和变长。如字符集C={a, b, c},等长编码{00, 01, 10},变长编码{0, 10, 11}。变长编码方案将出现频度高的字符编码设置成较短编码,将频度低的字符编码设置为较长编码,可有效节约存储空间。
前缀码是指,字符集中任一字符的编码都不是其他字符编码前缀的编码。如上例中变长编码。平均码长或文件总长最小的前缀编码称为最优前缀码。前缀码中具有典型的二叉树结构。
Huffman Tree指n个叶节点,权分别为 w 1 , w 2 , w 3 , . . . , w n w_1, w_2, w_3,...,w_n w1,w2,w3,...,wn 的二叉树中,带权路径长度WPL最小的二叉树,即最优二叉树。以分支权重代表出现频率,自底向上每次取最小的权,其根的权值为左右子树根的权值的和。如此重复,最终合并为一最优二叉树。该树叶子节点为字符,从根节点到叶子节点的路径(左0右1)即为该字符最优编码。
要求实现Huffman编码。

测试用例:
C={b,c,j,m,p},F={5,6,2,9,7},编码:b:101; c:00; j:100 m:11; p:01

问题分析:

题目要求明确,数据结构也已给出。c++STL中并没有封装树(虽然set和map是用红黑树实现的),所以需要自己定义并实现。获得最优编码的常见思路是构建最优二叉树之后,遍历得到编码。同时我们只能选择自底向上建立二叉树,所以得到的编码必然也是逆序,需要反转之后再存入节点中。

c++实现:

#include
#include
#include
#include
using namespace std;
struct Node{
    string ch, code;
    int lchild, rchild, parent, w;
};

void FindTwoSmallestNode(Node hufftree[], int &t1, int &t2, int k){
    int sw1, sw2, flag = 0;
    for(int i = 0; i < k; ++i){//满足条件的节点一定在k左边
        if(hufftree[i].parent == -1){//节点未被选择过
            if(flag == 0){
                sw1 = hufftree[i].w;
                t1 = i;
                ++flag;
                continue;
            }
            if(flag == 1){
                sw2 = hufftree[i].w;
                t2 = i;
                ++flag;
                continue;
            }
            if(hufftree[i].w != -1){//节点权值已初始化
                if(hufftree[i].w < sw1){
                    sw2 = sw1;
                    t2 = t1;
                    sw1 = hufftree[i].w;
                    t1 = i;
                }
                else if(hufftree[i].w < sw2){
                    sw2 = hufftree[i].w;
                    t2 = i;
                }
            }
        }
    }
    if(sw1 > sw2){
        int tmp = t1;
        t1 = t2;
        t2 = tmp;
    }
}

void Huffman_Tree(Node hufftree[], string chs[], int ws[], int n){
    for(int i = 0; i < 2 * n - 1; ++i){//初始化树
        hufftree[i].parent = -1;
        hufftree[i].lchild = -1;
        hufftree[i].rchild = -1;
        hufftree[i].ch = "";
    }
    for(int i = 0; i < n; ++i){//初始化叶子结点
        hufftree[i].ch = chs[i];
        hufftree[i].w = ws[i];
    }
    for(int i = n; i < 2 * n - 1; ++i){//形成树
        int t1 = 0, t2 = 0;
        FindTwoSmallestNode(hufftree, t1, t2, i);//将t1节点和t2节点合成为i节点
        hufftree[i].w = hufftree[t1].w + hufftree[t2].w;
        hufftree[t1].parent = i;
        hufftree[t2].parent = i;
        hufftree[i].lchild = t1;
        hufftree[i].rchild = t2;
    }
}

void Huffman_Code(Node hufftree[], int n){
    string s;
    for(int i = 0; i < n; ++i){
        s = "";
        int cur = i, p;
        while(hufftree[cur].parent != -1){//未到根节点
            p = hufftree[cur].parent;
            if(cur == hufftree[p].lchild){
                s += "0";
            }
            else{
                s += "1";
            }
            cur = p;
        }
        reverse(s.begin(), s.end());
        hufftree[i].code = s;
        cout<<setw(15)<<hufftree[i].ch<<setw(15)<<s<<endl;
    }
}

int main(){
    const int n = 5;
    Node huffmanTree[2 * n];//前n为叶子结点
    string chs[] = {"b", "c", "j", "m", "p"};
    int ws[] = {5, 6, 2, 9, 7};
    Huffman_Tree(huffmanTree, chs, ws, n);
    cout<<setw(15)<<"character"<<setw(15)<<"Huffman_Code"<<endl;
    Huffman_Code(huffmanTree, n);
    return 0;
}

你可能感兴趣的:(算法)