c++怎么实现哈夫曼树编码压缩_c++ 字符频率统计与变长编码【案例】
发布时间 - 2025-12-30 00:00:00 点击率:次哈夫曼压缩核心是按频率构建最小堆二叉树并生成唯一变长编码:需以unsigned char统计0–255字节频次,自定义priority_queue升序比较器,合并时权重小者为左子树(编0),大者为右(编1),空文件或单字符需特判;编码表须按“字符+长度+对齐比特”二进制格式写入头部,并校验编码唯一性。
怎么用 C++ 构建哈夫曼树并生成变长编码
核心是:先统计字符频率,再用优先队列(最小堆)构建带权路径最短的二叉树,最后递归/迭代生成每个字符的编码。关键不在“写树”,而在「保证构建过程严格按权重合并」和「编码方向不能反」。
常见错误是把左子树当 1、右子树当 0(或反之),导致解码失败;或者没处理单字符输入(比如文件只含一个 'a'),堆里只剩一个节点时直接崩溃。
- 用
std::priority_queue时必须自定义比较器,让它按freq升序——默认是大顶堆,得翻过来 - 树节点建议用结构体而非类,避免虚函数开销;叶子节点需存原始字符(
char或int),内部节点设为-1或0标记 - 编码生成推荐 DFS 递归:进左子树拼
"0",进右子树拼"1";别用 BFS,容易乱序且难回溯路径
struct Node {
int freq;
char ch;
Node* left;
Node* right;
Node(int f, char c) : freq(f), ch(c), left(nullptr), right(nullptr) {}
};
struct Compare {
bool operator()(Node* a, Node* b) { return a->freq > b->freq; }
};
// 构建过程节选
std::priority_queue, Compare> pq;
// ... 插入所有叶子节点
while (pq.size() > 1) {
Node* l = pq.top(); pq.pop();
Node* r = pq.top(); pq.pop();
Node* merged = new Node(l->freq + r->freq, '\0');
merged->left = l;
merged->right = r;
pq.push(merged);
}
字符频率统计要注意哪些边界情况
不能简单用 std::map 然后 fstream.get() 逐字节读——遇到空字符 '\0'、换行符 '\n'、高位字节(如 UTF-8 中文)会截断或误判。实际压缩对象是字节流,不是“字符流”。
- 必须以
unsigned char视角读取文件,映射到int范围[0, 255],用std::array统计最稳 - 文件末尾的
EOF不算有效字节,istream::get()返回int,需判断是否等于EOF再转unsigned char - 若输入为空文件,频率数组全零,后续建树要提前检查
total_count == 0并跳过压缩
std::arrayfreq{}; std::ifstream fin("input.bin", std::ios::binary); int byte; while ((byte = fin.get()) != EOF) { freq[static_cast (byte)]++; }
怎么把编码表高效存进压缩文件头部
不能直接写字符串如 "a:010\nb:11\n"——这本身就在膨胀数据。标准做法是:先写字符(1 字节),再写其编码长度(1 字节),最后写编码比特(按字节对齐,高位在前)。
例如字符 'x' 编码是 "1011"(4 位),就写:0x78('x' 的 ASCII)、0x04、0xB0(10110000,后 4 位补零凑满 1 字节)。解压时靠长度字段截取有效比特。
- 编码长度超过 8 位?正常,哈夫曼树
深度可能达 256,但实际英文文本一般 ≤ 32 - 务必在头部末尾写一个结束标记(如
0xFF),否则解压器无法知道头在哪结束 - 别用
std::string拼接编码比特——它按字节存,而你需要按位写入,得手写 bit writer 类或用std::vector(注意它不是容器,别用data())
为什么压缩后文件反而变大了
小文件(
- 测试时用 >10 KB 的纯英文文本(如《The Raven》),才能看到 30%~40% 压缩率
- 如果源文件已用
gzip压过,再套哈夫曼只会更大——熵已经极低,变长编码失去优势 - 真正工程中不会单独用哈夫曼,而是作为 DEFLATE(zip)的后端;自己实现时,至少加一层游程编码预处理,对付重复字节
最容易被忽略的是:没做「编码唯一性校验」。两个不同字符生成相同编码(比如都成了 "0"),整个压缩就不可逆。构建完树必须遍历所有叶子,确认无重复编码串——哪怕只是调试时用 std::set<:string> 临时塞一遍。
# node
# 编码
# 字节
# 后端
# c++
# ios
# 解压
# stream
# 为什么
# EOF
# String
# Array
# 字符串
# 结构体
# 递归
# char
# int
# 虚函数
# fstream
# 堆
# map
# 对象
# ASCII
# 子树
# 升序
# 英文
# 变长
# 自定义
# 时用
# 的是
# 成了
# 就在
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
如何在Windows服务器上快速搭建网站?
手机钓鱼网站怎么制作视频,怎样拦截钓鱼网站。怎么办?
Laravel如何实现全文搜索功能?(Scout和Algolia示例)
如何在橙子建站中快速调整背景颜色?
米侠浏览器网页图片不显示怎么办 米侠图片加载修复
三星网站视频制作教程下载,三星w23网页如何全屏?
Laravel怎么写单元测试_PHPUnit在Laravel项目中的基础测试入门
微信小程序 scroll-view组件实现列表页实例代码
怎么制作网站设计模板图片,有电商商品详情页面的免费模板素材网站推荐吗?
php485函数参数是什么意思_php485各参数详细说明【介绍】
Android自定义控件实现温度旋转按钮效果
PHP的CURL方法curl_setopt()函数案例介绍(抓取网页,POST数据)
Laravel如何创建自定义Facades?(详细步骤)
laravel怎么实现图片的压缩和裁剪_laravel图片压缩与裁剪方法
详解Oracle修改字段类型方法总结
JavaScript数据类型有哪些_如何准确判断一个变量的类型
Laravel如何配置Horizon来管理队列?(安装和使用)
详解Android图表 MPAndroidChart折线图
Windows10如何删除恢复分区_Win10 Diskpart命令强制删除分区
如何快速生成凡客建站的专业级图册?
Java类加载基本过程详细介绍
b2c电商网站制作流程,b2c水平综合的电商平台?
如何在 Pandas 中基于一列条件计算另一列的分组均值
如何在云主机上快速搭建网站?
如何基于PHP生成高效IDC网络公司建站源码?
如何在IIS7中新建站点?详细步骤解析
香港服务器网站推广:SEO优化与外贸独立站搭建策略
HTML5空格和nbsp有啥关系_nbsp的作用及使用场景【说明】
详解Android——蓝牙技术 带你实现终端间数据传输
Bootstrap整体框架之JavaScript插件架构
如何确保FTP站点访问权限与数据传输安全?
HTML5打空格有哪些误区_新手常犯的空格使用错误【技巧】
专业企业网站设计制作公司,如何理解商贸企业的统一配送和分销网络建设?
南京网站制作费用,南京远驱官方网站?
佛山网站制作系统,佛山企业变更地址网上办理步骤?
JavaScript Ajax实现异步通信
详解MySQL数据库的安装与密码配置
移动端脚本框架Hammer.js
如何正确下载安装西数主机建站助手?
laravel怎么在请求结束后执行任务(Terminable Middleware)_laravel Terminable Middleware请求结束任务执行方法
网站视频制作书签怎么做,ie浏览器怎么将网站固定在书签工具栏?
专业商城网站制作公司有哪些,pi商城官网是哪个?
开心动漫网站制作软件下载,十分开心动画为何停播?
Chrome浏览器标签页分组怎么用_谷歌浏览器整理标签页技巧【效率】
Laravel如何实现URL美化Slug功能_Laravel使用eloquent-sluggable生成别名【方法】
网站制作怎么样才能赚钱,用自己的电脑做服务器架设网站有什么利弊,能赚钱吗?
Laravel如何构建RESTful API_Laravel标准化API接口开发指南
Linux安全能力提升路径_长期防护思维说明【指导】
Laravel如何使用Blade组件和插槽?(Component代码示例)
EditPlus中的正则表达式实战(6)


深度可能达 256,但实际英文文本一般 ≤ 32