c++实现霍夫曼编码

2023-11-15

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

考虑到指针可能对新手不太友好,这里用的是vector容器(用法类似数组,可以动态扩容)存储树形结构,大致原理就是n号结点的左右子树分别是2n和2n+1号结点。

HuffmanCode.h

#include <stack>
#include <cmath>
#include "myhead.h"

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

// 选择最小的权重节点
void findMin(vector<Node> 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<Node>& 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<Node>& ht, vector<string>& code, int n) {
    code.push_back("");  // 0下标的占位
    int p;
    int c;
    for (int i = 1; i <= n; i++) {
        stack<char> 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<string> 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 <iostream>
#include <string>
#include <vector>
#include <time.h>
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<string> getChar() {
    vector<string> 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 <iostream>
#include "HuffmanCode.h"

int main() {
    string a = rand_str(5);
    vector<string> 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<Node> ht;
        vector<string> 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;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

c++实现霍夫曼编码 的相关文章

  • C 编程 - 文件 - fwrite

    我有一个关于编程和文件的问题 while current NULL if current gt Id Doctor 0 current current gt next id doc current gt Id Doctor if curre
  • 为什么 C# Array.BinarySearch 这么快?

    我已经实施了一个很简单用于在整数数组中查找整数的 C 中的 binarySearch 实现 二分查找 static int binarySearch int arr int i int low 0 high arr Length 1 mid
  • GLKit的GLKMatrix“列专业”如何?

    前提A 当谈论线性存储器中的 列主 矩阵时 列被一个接一个地指定 使得存储器中的前 4 个条目对应于矩阵中的第一列 另一方面 行主 矩阵被理解为依次指定行 以便内存中的前 4 个条目指定矩阵的第一行 A GLKMatrix4看起来像这样 u
  • 为什么两个不同的 Base64 字符串的转换会返回相等的字节数组?

    我想知道为什么从 base64 字符串转换会为不同的字符串返回相同的字节数组 const string s1 dg const string s2 dq byte a1 Convert FromBase64String s1 byte a2
  • 按成员序列化

    我已经实现了template
  • 如何使用 ICU 解析汉字数字字符?

    我正在编写一个使用 ICU 来解析由汉字数字字符组成的 Unicode 字符串的函数 并希望返回该字符串的整数值 五 gt 5 三十一 gt 31 五千九百七十二 gt 5972 我将区域设置设置为 Locale getJapan 并使用
  • OleDbDataAdapter 未填充所有行

    嘿 我正在使用 DataAdapter 读取 Excel 文件并用该数据填充数据表 这是我的查询和连接字符串 private string Query SELECT FROM Sheet1 private string ConnectStr
  • 不同枚举类型的范围和可转换性

    在什么条件下可以从一种枚举类型转换为另一种枚举类型 让我们考虑以下代码 include
  • C#中如何移动PictureBox?

    我已经使用此代码来移动图片框pictureBox MouseMove event pictureBox Location new System Drawing Point e Location 但是当我尝试执行时 图片框闪烁并且无法识别确切
  • 重载<<的返回值

    include
  • 如何在整个 ASP .NET MVC 应用程序中需要授权

    我创建的应用程序中 除了启用登录的操作之外的每个操作都应该超出未登录用户的限制 我应该添加 Authorize 每个班级标题前的注释 像这儿 namespace WebApplication2 Controllers Authorize p
  • 如何序列化/反序列化自定义数据集

    我有一个 winforms 应用程序 它使用强类型的自定义数据集来保存数据进行处理 它由数据库中的数据填充 我有一个用户控件 它接受任何自定义数据集并在数据网格中显示内容 这用于测试和调试 为了使控件可重用 我将自定义数据集视为普通的 Sy
  • 这些作业之间是否存在顺序点?

    以下代码中的两个赋值之间是否存在序列点 f f x 1 1 x 2 不 没有 在这种情况下 标准确实是含糊不清的 如果你想确认这一点 gcc 有这个非常酷的选项 Wsequence point在这种情况下 它会警告您该操作可能未定义
  • Windows 窗体:如果文本太长,请添加新行到标签

    我正在使用 C 有时 从网络服务返回的文本 我在标签中显示 太长 并且会在表单边缘被截断 如果标签不适合表单 是否有一种简单的方法可以在标签中添加换行符 Thanks 如果您将标签设置为autosize 它会随着您输入的任何文本自动增长 为
  • 覆盖子类中的字段或属性

    我有一个抽象基类 我想声明一个字段或属性 该字段或属性在从该父类继承的每个类中具有不同的值 我想在基类中定义它 以便我可以在基类方法中引用它 例如覆盖 ToString 来表示 此对象的类型为 property field 我有三种方法可以
  • 链接器错误:已定义

    我尝试在 Microsoft Visual Studio 2012 中编译我的 Visual C 项目 使用 MFC 但出现以下错误 error LNK2005 void cdecl operator new unsigned int 2
  • 对现有视频添加水印

    我正在寻找一种用 C 在视频上加水印的方法 就像在上面写文字一样 图片或文字标签 我该怎么做 谢谢 您可以使用 Nreco 视频转换器 代码看起来像 NReco VideoConverter FFMpegConverter wrap new
  • WPF/C# 将自定义对象列表数据绑定到列表框?

    我在将自定义对象列表的数据绑定到ListBox in WPF 这是自定义对象 public class FileItem public string Name get set public string Path get set 这是列表
  • cmake 将标头包含到每个源文件中

    其实我有一个简单的问题 但找不到答案 也许你可以给我指一个副本 所以 问题是 是否可以告诉 cmake 指示编译器在每个源文件的开头自动包含一些头文件 这样就不需要放置 include foo h 了 谢谢 CMake 没有针对此特定用例的
  • 对来自流读取器的过滤数据执行小计

    编辑问题未得到解答 我有一个基于 1 个标准的过滤输出 前 3 个数字是 110 210 或 310 给出 3 个不同的组 从流阅读器控制台 问题已编辑 因为第一个答案是我给出的具体示例的字面解决方案 我使用的实际字符串长度为 450 个

随机推荐

  • Vuforia VirtualButtons 虚拟按键

    Vuforia VirtualButtons 虚拟按键 注意 1 不介意使用Unity自带的Vuforia 引擎 否则下载商场里的示例项目时可能会因为版本问题产生报错 推荐直接从商店下载示例的引擎使用 2 不推荐下载目前最新9 2版引擎 导
  • Matlab 函数进阶:使用匿名函数和内嵌函数处理多变量传递问题(Matlab 7.0以上)

    from http asc 2dark org node 70 Matlab 函数进阶 使用匿名函数 Anonymous Function 和内嵌函数 Nested Function 处理多变量传递问题 Matlab 7 0以上 问题 有一
  • WPF工控组态软件之温度计

    WPF以其丰富灵活的控件样式设计 相较于WinForm而言 一直是工控组态软件的宠儿 经过前两文章的学习 已经对WPF开发工控组态软件有了一个基本的了解 今天继续学习温度计的开发 仅供学习分享使用 如有不足之处 还请指正 涉及知识点 在本示
  • NotePad++遇到电脑重启或者突然崩溃,已经打开的文件信息和未保存的文件没了怎么办?

    1 崩溃前打开的文件列表信息可以在如下文件中找到 C Users 你当前用户的用户名 AppData Roaming Notepad session xml 里面存放了文件打开列表 2 崩溃前未保存的文件可以在如下路径进行恢复 C User
  • Node.js Buffer的简单使用

    Node js 目前支持的字符编码包括 ascii 仅支持 7 位 ASCII 数据 如果设置去掉高位的话 这种编码是非常快的 utf8 多字节编码的 Unicode 字符 许多网页和其他文档格式都使用 UTF 8 utf16le 2 或
  • JVM 垃圾回收

    JVM 垃圾回收 写在前面 本节常见面试题 问题答案在文中都有提到 如何判断对象是否死亡 两种方法 简单的介绍一下强引用 软引用 弱引用 虚引用 虚引用与软引用和弱引用的区别 使用软引用能带来的好处 如何判断一个常量是废弃常量 如何判断一个
  • Linux基线检查( 一)

    什么是基线 即安全基线配置 诸如操作系统 中间件和数据库的一个整体配置 这个版本中各项配置都符合安全方面的标准 比如在系统安装后需要按安全基线标准 将新机器中各项配置调整到一个安全 高效 合理的数值 风险分类 系统 等保三级 CentOS
  • linux 普通用户 Cannot create VM thread. Out of system resources.

    在linux系统下开发 使用普通用户进行应用部署 出现JVM内存问题如下 Error occurred during initialization of VM Cannot create VM thread Out of system re
  • Basic Level 1023 组个最小数 (20分)

    题目 给定数字 0 9 各若干个 你可以以任意顺序排列这些数字 但必须全部使用 目标是使得最后得到的数尽可能小 注意 0 不能做首位 例如 给定两个 0 两个 1 三个 5 一个 8 我们得到的最小的数就是 10015558 现给定数字 请
  • Python学习笔记第六十四天(Matplotlib 网格线)

    Python学习笔记第六十四天 Matplotlib 网格线 普通网格线 样式网格线 后记 Matplotlib 网格线 我们可以使用 pyplot 中的 grid 方法来设置图表中的网格线 grid 方法语法格式如下 matplotlib
  • 系统架构设计师(第二版)学习笔记----信息系统基础

    原文链接 系统架构设计师 第二版 学习笔记 信息系统基础 文章目录 一 信息系统概述 1 1 信息系统的5个基本功能 1 2 信息系统发展阶段 1 3 初始阶段的主要特点 1 4 传播阶段的主要特点 1 5 控制阶段的主要特点 1 6 集成
  • lock_guard和unique_lock

    锁 锁用来在多线程访问同一个资源时防止数据竞险 保证数据的一致性访问 多线程本来就是为了提高效率和响应速度 但锁的使用又限制了多线程的并行执行 这会降低效率 但为了保证数据正确 不得不使用锁 它们就是这样纠缠 本文主要讨论 c 11 中的两
  • 网络安全工程师的职责 103.219.28.X

    网络安全工程师是专门从事网络安全防护 攻击和事故处理等工作的技术人员 其主要职责包括 评估和分析网络漏洞和威胁情况 制定网络安全策略和方案 开发和实施安全解决方案 监控和维护网络安全系统 应对网络攻击和事故等 他们需要掌握网络安全技术 网络
  • DateTime关于时区的学习

    一 时区的概念 首先来了解时区的概念 为了解决世界不同各地在时间上的差异 人们定义了时区 时区是地球上的区域使用同一个时间定义 人们将时区分为24个 它们是中时区 零时区 东1 12区 西1 12区 每个时区横跨经度15度 时间正好是1小时
  • SQL Server临时表

    创建临时表 方法一 create table 临时表名 字段1 约束条件 字段2 约束条件 create table 临时表名 字段1 约束条件 字段2 约束条件 方法二 select into 临时表名 from 你的表 select i
  • 【JAVA设计模式】之桥接模式(BridgePattern)

    桥接模式的作用是将两样东西连接起来 比如桥梁把河的两岸连接起来 桥接模式把 类的功能层次结构 和 类的实现层次结构 连接起来 类的层次结构 类的功能层次 父类通过定义抽象方法来定义接口API 类的实现层次 子类通过实现具体方法来实现接口AP
  • FP64、FP32、FP16、FP8简介

    目录 1 单精度浮点数FP32的表示 2 半精度浮点数FP16的表示 3 双精度浮点数FP64的表示 4 FP8 5 写在最后 1 单精度浮点数FP32的表示 浮点数由三部分组成 符号位 指数部分 尾数部分 以单精度浮点数为例 如图所示 符
  • 好多粉使用百度OCPC api提交后如何手动选择有效咨询回传?

    推广每天会有复制 虽然绝大部分是正常的数据 但也有可能有的是同行刷的 有的是没有添加成功的数据 如果不处理 会造成无效数据上传 只能投放的AI可能就要被某些别有用心的人玩坏了 所以我们为解决这个痛点 开发了手动回传功能 并且系统自动帮你标识
  • VS2019现有项目添加Qt界面

    目录 Qt安装教程 Qt下载 Qt安装 VS Qt插件 配置Qt插件 配置包含目录及链接器 添加QT界面 添加UI界面 配置文件属性 编译UI文件 添加 h和 cpp文件 简单运行测试 双击ui文件打开Qt Designer闪退问题 与VS
  • c++实现霍夫曼编码

    c 实现霍夫曼编码 计算信源的熵 平均码长 编码效率 冗余度与压缩比 考虑到指针可能对新手不太友好 这里用的是vector容器 用法类似数组 可以动态扩容 存储树形结构 大致原理就是n号结点的左右子树分别是2n和2n 1号结点 Huffma