栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

c++实现霍夫曼编码

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

c++实现霍夫曼编码

c++实现霍夫曼编码,计算信源的熵、平均码长、编码效率、冗余度与压缩比

HuffmanCode.h

#include 
#include 
#include "myhead.h"

typedef struct {
    char ch;  // 存储的字符
    int weight;  // 权重
    int parent;  // 父节点
    int lChild;
    int rChild;
} Node;

// 选择最小的权重节点
void findMin(vector ht, int n, int* s1, int* s2) {
    int min = INT_MAX;
    for (int i = 1; i <= n; i++) {
        if (ht[i].parent == 0) { // 如果没有父节点
            min = i;
            break;
        }
    }

    for (int i = 1; i <= n; i++) {
        if (ht[i].parent == 0) {
            if (ht[i].weight < ht[min].weight) {
                min = i;  // 权重比记录更小,则更新
            }
        }
    }
    *s1 = min;
    //遍历全部结点
    for (int i = 1; i <= n; i++) {
        if (ht[i].parent == 0 && i != (*s1)) {
            min = i;
            break;
        }
    }
    for (int i = 1; i <= n; i++) {
        if (ht[i].parent == 0 && i != (*s1)) {
            if (ht[i].weight < ht[min].weight) {
                min = i;  // 更小则更新
            }
        }
    }
    *s2 = min;
}

// 生成哈夫曼树
void generateTree(vector& ht, int times[], int n) {
    int m = 2 * n - 1;  // 节点总数
    int s1;
    int s2;
    Node firstNode;  // 填充下标为0
    firstNode.ch = '0';
    firstNode.weight = 0;
    firstNode.lChild = 0;
    firstNode.rChild = 0;
    firstNode.parent = 0;
    ht.push_back(firstNode);
    for (int i = 1; i <= n; i++) {
        Node temp;
        temp.ch = char(96 + i);// a的ASCII码为97
        temp.weight = times[i];
        temp.lChild = 0;
        temp.rChild = 0;
        temp.parent = 0;
        ht.push_back(temp);
    }
    for (int i = n + 1; i <= m; i++) {
        Node temp;
        temp.ch = '0';
        temp.weight = 0;
        temp.lChild = 0;
        temp.rChild = 0;
        temp.parent = 0;
        ht.push_back(temp);
    }
    for (int i = n + 1; i <= m; i++) {
        findMin(ht, i - 1, &s1, &s2);  // 选择最小的两个权重节点
        ht[s1].parent = i;
        ht[s2].parent = i;
        ht[i].lChild = s1;
        ht[i].rChild = s2;
        ht[i].weight = ht[s1].weight + ht[s2].weight;  //更新权重
    }
}

// 从叶节点到根节点找霍夫曼编码
void generateCode(vector& ht, vector& code, int n) {
    code.push_back("");  // 0下标的占位
    int p;
    int c;
    for (int i = 1; i <= n; i++) {
        stack st;  // 因为是逆向存储,所以使用栈
        int start = n - 1;
        for (c = i, p = ht[i].parent; p != 0; c = p, p = ht[p].parent) {
            if (ht[p].lChild == c) {
                st.push('0');  // 是左孩子则压入0
            }
            else {
                st.push('1');
            }
        }
        int s_len = st.size();
        string hfm_code = "";
        // 从栈中取出
        for (int i = 0; i < s_len; i++) {
            hfm_code += st.top();
            st.pop();
        }
        code.push_back(hfm_code);
    }
    cout << "霍夫曼编码结果如下" << endl;
    for (int i = 1; i <= n; i++)
    {
        cout << ht[i].ch << ":" << code[i] << endl;
    }
}

// 计算信源的熵
double CalEntropy(int times[], int len, int n) {
    double temp = 0;
    for (int i = 1; i < len; i++) {
        double p = double(times[i]) / n;
        temp += p * log2(p);
    }
    temp = -temp;
    return temp;
}

// 计算霍夫曼编码的平均码长
double CalAvgLen(vector code, int times[], int len, int n) {
    double temp = 0;
    for (int i = 1; i < len; i++) {
        int len = code[i].length();
        double p = double(times[i]) / n;
        temp += double(len) * p;
    }
    return temp;
}

myhead.h

#include 
#include 
#include 
#include 
using namespace std;

// 随机生成字符串,这里只使用a~z,可以使用ASCII码这样就是全部字符,但是会增加遍历时间
string rand_str(int len) {
    string str;
    char c;
    for (int i = 0; i < len; i++) {
        c = 'a' + rand() % 26;
        str.push_back(c);
    }
    return str;
}

// 生成十条随机字符串
vector getChar() {
    vector ten_str;
    srand(unsigned int(time(NULL)));  // 转换为无符号整型避免安全提示
    for (int i = 0; i < 10; i++) {
        int length = 1000 + rand() % 9000;
        ten_str.push_back(rand_str(length));
        // cout << length << endl;
    }
    return ten_str;
}

// 统计每个字符出现的次数
void getNum(string str) {
    int nums[26] = { 0 };
    for (unsigned int i = 0; i < str.length(); i++) {
        nums[int(str[i]) - 97] ++;  // ASCII码中a为97
    }
    cout << "a~z的出现次数分别为:";
    for (int i = 0; i < 26; i++) {
        // cout << char(i + 97) << "的次数为:" << nums[i] << endl;
        cout << nums[i] << "     ";
    }
    cout << endl;
}
#include 
#include "HuffmanCode.h"

int main() {
    string a = rand_str(5);
    vector ten_str = getChar();  // 生成十条长度在1000~10000的字符串
    for (int i = 0; i < 10; i++) {
        cout << "第" << i + 1 << "条随机字符串长度为" << ten_str[i].length() << endl;
        // getNum(ten_str[i]);

        vector ht;
        vector code;
        // 因为字符串非常长,所以默认随机生成的时候全部26个字符都包含了,简化算法复杂程度,如果字符串很短需要遍历查询
        int n = 26;
        int times[27] = { 0 };  // 出现的次数
        for (unsigned int j = 0; j < ten_str[i].length(); j++) {
            times[int(ten_str[i][j]) - 96] ++;  // ASCII码中a为97
        }
        cout << "a~z的出现次数分别为:";
        for (int i = 1; i < 27; i++) {
            cout << times[i] << " ";
        }
        cout << endl;
        generateTree(ht, times, n);
        generateCode(ht, code, n);

        // 计算信源的熵
        double entropy = CalEntropy(times, 27, ten_str[i].length());
        cout << "信源的熵为:" << entropy << endl;

        //计算霍夫曼编码的平均码长
        double avg_len = CalAvgLen(code, times, 27, ten_str[i].length());
        cout << "平均码长为:" << avg_len << endl;

        cout << "霍夫曼编码效率为:" << entropy / avg_len << endl;
        cout << "冗余度为:" << (1 - entropy / avg_len) * 100 << "%" << endl;
        cout << "压缩比为:" << 5 / avg_len << endl;
        cout << endl;
    }
    return 0;
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1022086.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号