哈夫曼树的构造、编码和解码

被问了一天哈夫曼树=_=
于是迫不得已敲了一个…

首先是读字符,统计文章中n种字符每种出现的次数,然后降序排序。
然后把出现次数作为权值,建立n个叶子结点。把所有叶子结点插入小根堆中,每次取出两个,用二者权值之和构造新的节点,作为这二者的父亲节点,最后一个点作为根,记录下来。
虽然树是自底向上建的,但实际使用的时候只用从根往下跑,所以不必记录父节点。
树建完跑dfs就能得到所有叶子结点的huffman编码(左0右1),跑到根的时候直接哈希表记录该字符对应的编码,对文章编码时直接调用哈希表即可。
解码时把01串放到huffman树上跑,跑到根就输出对应字符,并把当前位置重置为root。

#include <queue>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <unordered_map>

using namespace std;

void read(char text[])//读取文件信息,结果保存在text中
{
    FILE *fp = fopen("test.txt", "r");
    memset(text, 0, sizeof(text));
    char ch;
    int len = 0;
    fscanf(fp, "%c", &ch);
    while (!feof(fp))
    {
        text[len++] = ch;
        fscanf(fp, "%c", &ch);
    }
    fclose(fp);
}

typedef struct
{
    char c;//字符
    int cnt;//出现次数
    string code;
} ch;

bool cmp(ch a, ch b)
{
    return a.cnt > b.cnt;//排序规则
}

ch c[300];

int count(char text[])//统计词频并输出
{
    int len = strlen(text);

    for (int i = 0; i < 300; ++i)
        c[i].c = char(i);

    for (int i = 0; i < len; ++i)
        c[text[i]].cnt++;
    sort(c, c + 300, cmp);//按出现次数降序排序

    for (int i = 0; i < 300; ++i)
    {
        if (c[i].cnt == 0)
            return i;//返回出现次数大于0的词的数量(即种数)
        printf("%c: %d\n", c[i].c, c[i].cnt);
    }
}

struct node
{
    int w, id;
    int lson, rson;

    node()
    {
        lson = rson = 0;
    }

    bool operator<(const node b) const
    {
        return w > b.w;
    }
};

int buildHuffmanTree(int n, node huffNode[])
{
    int id = 0;//下一个未使用过的huffNode数组元素的下标
    priority_queue<node> que;
    for (int i = 0; i < n; ++i)//下标为0~n-1的huffNode是叶子
    {
        huffNode[id].w = c[i].cnt;
        huffNode[id].id = i;
        que.push(huffNode[id]);
        ++id;
    }
    node a, b;
    int root;
    while (true)
    {
        a = que.top();
        que.pop();
        b = que.top();//取出权值最小的两个点
        que.pop();
        huffNode[id].lson = a.id;
        huffNode[id].rson = b.id;
        huffNode[id].w = huffNode[a.id].w + huffNode[b.id].w;
        huffNode[id].id = id;
        if (que.empty())//当前点为根
        {
            root = id;
            break;
        }
        que.push(huffNode[id]);
        ++id;
    }
    return root;
}

unordered_map<char, string> mp;//建立字符到编码的映射关系

void dfs(int now, node huffmanNode[], string code)
{
    if (!huffmanNode[now].lson && !huffmanNode[now].rson)//没孩子节点,即叶子
    {
        mp[c[huffmanNode[now].id].c] = code;
        return;
    }
    dfs(huffmanNode[now].lson, huffmanNode, code + "0");
    dfs(huffmanNode[now].rson, huffmanNode, code + "1");
}

void print()
{
    for (int i = 0; i < 300; ++i)
    {
        if (!c[i].cnt)
            break;
        printf("%c的出现次数为%d, huffman编码为: %s\n", c[i].c, c[i].cnt, mp[c[i].c].c_str());
    }
}

void decode(char code[], node huffNode[], int root, int n)
{
    int len = strlen(code);
    int now = root;
    for (int i = 0; i < len; ++i)
    {

        if (code[i] == '0')
            now = huffNode[now].lson;
        else
            now = huffNode[now].rson;
        if (huffNode[now].id < n)
        {
            printf("%c", c[huffNode[now].id].c);
            now = root;
        }
    }
}

int main()
{
    char text[10000];
    read(text);
    int n = count(text);
    node *huffNode = new node[2 * n + 1];
    int root = buildHuffmanTree(n, huffNode);
    string code = "";
    dfs(root, huffNode, code);//dfs huffman树,获取字符的编码
    print();//输出字符的编码
    int len = strlen(text);
    char *encode = new char[50000];
    memset(encode, 0, sizeof(encode));
    for (int i = 0; i < len; ++i)
        strcat(encode, mp[text[i]].c_str());
    printf("%s\n", encode);
    puts("");
    printf("%s\n", text);
    decode(encode, huffNode, root, n);
    return 0;
}
posted @ 2019-06-01 20:34  Apale  阅读(230)  评论(0编辑  收藏  举报