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 】
相关推荐:
手机软键盘弹出时影响布局的解决方法
怎么用AI帮你为初创公司进行市场定位分析?
php做exe能调用系统命令吗_执行cmd指令实现方式【详解】
公司网站制作价格怎么算,公司办个官网需要多少钱?
DeepSeek是免费使用的吗 DeepSeek收费模式与Pro版本功能详解
Windows11怎样设置电源计划_Windows11电源计划调整攻略【指南】
Laravel如何生成PDF或Excel文件_Laravel文档导出工具与使用教程
详解CentOS6.5 安装 MySQL5.1.71的方法
详解阿里云nginx服务器多站点的配置
1688铺货到淘宝怎么操作 1688一键铺货到自己店铺详细步骤
什么是javascript作用域_全局和局部作用域有什么区别?
简历没回改:利用AI润色让你的文字更专业
Laravel Session怎么存储_Laravel Session驱动配置详解
宙斯浏览器怎么屏蔽图片浏览 节省手机流量使用设置方法
PHP的CURL方法curl_setopt()函数案例介绍(抓取网页,POST数据)
如何在沈阳梯子盘古建站优化SEO排名与功能模块?
HTML透明颜色代码怎么让图片透明_给img元素加透明色的技巧【方法】
,交易猫的商品怎么发布到网站上去?
Laravel如何为API生成Swagger或OpenAPI文档
Mybatis 中的insertOrUpdate操作
Laravel如何使用Seeder填充数据_Laravel模型工厂Factory批量生成测试数据【方法】
济南网站建设制作公司,室内设计网站一般都有哪些功能?
香港服务器建站指南:外贸独立站搭建与跨境电商配置流程
详解Huffman编码算法之Java实现
如何快速打造个性化非模板自助建站?
如何在宝塔面板中创建新站点?
如何在阿里云部署织梦网站?
Laravel事件和监听器如何实现_Laravel Events & Listeners解耦应用的实战教程
Laravel如何使用Gate和Policy进行权限控制_Laravel权限判定与策略规则配置
Laravel怎么使用Collection集合方法_Laravel数组操作高级函数pluck与map【手册】
如何用wdcp快速搭建高效网站?
Win11怎么设置默认图片查看器_Windows11照片应用关联设置
Laravel如何发送系统通知?(Notification渠道示例)
如何在万网主机上快速搭建网站?
node.js报错:Cannot find module 'ejs'的解决办法
微信小程序 require机制详解及实例代码
Laravel如何使用Passport实现OAuth2?(完整配置步骤)
如何在景安服务器上快速搭建个人网站?
Laravel如何使用Guzzle调用外部接口_Laravel发起HTTP请求与JSON数据解析【详解】
想要更高端的建设网站,这些原则一定要坚持!
Laravel如何实现RSS订阅源功能_Laravel动态生成网站XML格式订阅内容【教程】
关于BootStrap modal 在IOS9中不能弹出的解决方法(IOS 9 bootstrap modal ios 9 noticework)
如何用花生壳三步快速搭建专属网站?
智能起名网站制作软件有哪些,制作logo的软件?
太平洋网站制作公司,网络用语太平洋是什么意思?
详解Android图表 MPAndroidChart折线图
大型企业网站制作流程,做网站需要注册公司吗?
JavaScript中如何操作剪贴板_ClipboardAPI怎么用
如何快速生成高效建站系统源代码?
网站制作大概要多少钱一个,做一个平台网站大概多少钱?


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