文件的哈夫曼编码与解码

编码过程中,踩了一些小坑,做下记录:

  • 1.全局变量countstd:count矛盾,建议用其他变量名。
  • 2.内存泄漏问题 注意空间要开够 指针不可越界 main函数内开辟的栈空间大小一般为8MB 若要开辟较大的数组 请去main函数之外
  • 3.编译器错误 推荐大家使用教新的较稳定的编译器
  • 4.文件操作 打开后记得关闭 否则会占用系统资源
  • 5.申请完空间,要记得释放,养成习惯。释放函数不可张冠李戴(留心编译器的Warning)。malloc/free,new/delete要配对使用。具体原因可参考 这篇文章

编码要求及任务:

准备一个字符文件,要求:

  1. 统计该文件中各种字符的频率
  2. 对各字符进行 Huffman编码,显示每个字符的编码
  3. 以及将该文件翻译成 Huffman编码文件
  4. 再将 Huffman编码文件翻译成源文件
  5. 显示每个字符以一个字节进行二进制编码后的编码文件

实现步骤可分为:

  1. 统计被编码文件中个字符出现的频数,即统计权重
  2. 根据权重,构造哈夫曼树,进行哈夫曼编码
  3. 读取文件进行二进制编码
  4. 读取文件,将每个字符匹配哈夫曼编码,写入新文件,即完成编码
  5. 读取编码文件,根据哈夫曼编码进行解码,并写入新文件
  6. 对比二进制编码和哈夫曼编码后的文件字节大小,并计算压缩率

首先,准备一个源文件

这里我准备了一首小诗,写入文件,并将其命名为poem.txt

If I could save time in a bottle
the first thing that I'd like to do
is to save every day until eternity passes away
just to spend them with you
if I could make days last forever
if words could make wishes come true
I'd save every day like a treasure and then
again I would spend them with you

构建哈夫曼节点

// 定义哈夫曼树节点
typedef struct {int weight;int parent;int l_child;int r_child;char data;
} HTNode, * HuffmanTree;
typedef char** HuffmanCode;

频数统计

//统计该文件中各种字符的频率
void frequencyRecord(HuffmanTree& HT) {HuffmanTree TEMP;TEMP = new HTNode[130];for (int i = 0; i < 130; ++i) {TEMP[i].weight = 0;}ifstream originFile("poem.txt");originFile.seekg(0);if (!originFile) {cout << "Can't find the file!" << endl;} else {char _data;cin.unsetf(ios::skipws);while (!originFile.eof()) {if (originFile.get(_data)) {TEMP[_data].data = _data;TEMP[_data].weight++;}}originFile.close();}for (int i = 0; i < 130; ++i) {if (TEMP[i].weight != 0) {N++;}}HT = new HTNode[2 * N];int k = 1;for (int i = 0; i < 130; ++i) {if (TEMP[i].weight != 0) {HT[k++] = TEMP[i];}}
}

哈夫曼编码

//对各字符进行 Huffman编码,显示每个字符的编码
void HuffmanCoding(HuffmanTree& HT, HuffmanCode& HC) {int m = 2 * N - 1;for (int i = 1; i <= N; ++i) {HT[i].parent = 0;HT[i].l_child = 0;HT[i].r_child = 0;}for (int i = N + 1; i <= m; ++i) {HT[i].weight = 0;HT[i].parent = 0;HT[i].l_child = 0;HT[i].r_child = 0;HT[i].data = '#';}int child1, child2;for (int i = N + 1; i <= m; i++) {select(HT, i - 1, child1, child2);HT[child1].parent = i;HT[child2].parent = i;HT[i].l_child = child1;HT[i].r_child = child2;HT[i].weight = HT[child1].weight + HT[child2].weight;}HC = new char* [N + 1];char* cd = new char[N];cd[N - 1] = '\0';int start, c, f;for (int i = 1; i <= N; i++) {start = N - 1;for (c = i, f = HT[i].parent; f != 0; c = f, f = HT[f].parent) {if (HT[f].l_child == c) cd[--start] = '0';else cd[--start] = '1';}HC[i] = new char[N - start];strcpy(HC[i], &cd[start]);}delete[] cd;for (int i = 1; i <= N; i++) {if (HT[i].data == '\n') {cout << "回车" << " " << HC[i] << endl;} else if (HT[i].data == ' ') {cout << "空格" << " " << HC[i] << endl;} else {cout << HT[i].data << " " << HC[i] << endl;;}}
}
其中,寻找最小的两个叶子节点功能函数为
//找出最小的两个叶子节点
void select(HuffmanTree HT, int num, int& child1, int& child2) {child1 = child2 = 0;int w1 = 0, w2 = 0;//Start finding...for (int i = 1; i <= num; ++i) {if (HT[i].parent == 0) {if (child1 == 0) {child1 = i;w1 = HT[i].weight;continue;}if (child2 == 0) {child2 = i;w2 = HT[i].weight;continue;}if (w1 > w2 && w1 > HT[i].weight) {child1 = i;w1 = HT[i].weight;continue;}if (w2 > w1 && w2 > HT[i].weight) {child2 = i;w2 = HT[i].weight;continue;}}}// 使得w1永远小于w2int temp;if (w1 > w2) {temp = child1;child1 = child2;child2 = temp;}
}

编码并写入文件

//将该文件翻译成 Huffman编码文件
void zip(HuffmanTree& HT, HuffmanCode& HC, vector<string>& code) {ofstream codeFile("codefile.txt");ifstream originFile("poem.txt");if (!codeFile) {cout << "Can't find the file!" << endl;} else {char _data;cin.unsetf(ios::skipws);while (!originFile.eof()) {if (originFile.get(_data)) {for (int i = 1; i <= N; ++i) {if (HT[i].data == _data) {codeFile << HC[i];code.push_back(HC[i]);}}}}}codeFile.close();
}

打开编码文件,进行解码

//再将 Huffman编码文件翻译成源文件
void unzip(HuffmanTree& HT, HuffmanCode& HC, vector<string>& code) {ofstream decodeFile("decodefile.txt");if (!decodeFile) {cout << "Can't find the file!" << endl;} else {vector<string>::iterator v = code.begin();while (v != code.end()) {for (int i = 1; i <= N; ++i) {if (HC[i] == *v) {decodeFile << HT[i].data;}}v++;}}decodeFile.close();
}

二进制编码

//显示每个字符以一个字节进行二进制编码后的编码文件
void binaryCode() {ofstream binaryFile("binaryfile.txt");ifstream originFile("poem.txt");originFile.seekg(0);if (!originFile) {cout << "Can't find the file!" << endl;} else {char _data;cin.unsetf(ios::skipws);while (!originFile.eof()) {if (originFile.get(_data)) {bitset<8> data(_data);binaryFile << data;}}originFile.close();}
}

运行结果

读取源文件,二进制编码

统计字符频次,得到编码表

编码源文件及编码结果

解码编码文件及解码结果

压缩效果

发现这里实际大小与占用空间不一样?这篇文章可以解答你的疑惑

小结

通过编写利用哈夫曼算法实现的文件编码解码小工具,可加深对哈夫曼算法的理解,以及编码的熟练度。

同时,体会到通过算法减少文本空间,降低计算机磁盘负荷的妙处,我们需要优秀的算法来提升计算机性能。在实际的压缩算法中虽然很少听到哈夫曼编码,但其实它并没有被淘汰,而是作为核心的算法,结合了其他的压缩算法,实现对文件(文本,PPT,图片,电影等)的压缩,给日常生活带来极大便利。

本人的小工具仅针对英文大小字母及' '\n' ' '字符针对性的进行了哈夫曼编码,若想实现中文及各种支持语言的编码,可在此代码基础上,进行优化,源码地址为https://github.com/Cheung0-bit/HuffmanTreeCoding,欢迎PR;

文件的哈夫曼编码与解码相关推荐

  1. 自适应(动态)哈夫曼编码与解码过程

    自适应(动态)哈夫曼编码与解码过程 自定义哈夫曼编码,预先不知道各种符号的出现频率,编码树的初始状态只包含一个叶节点,即NYT(Not Yet Transmitted),NYT是一个逸出码,不同于任何 ...

  2. 数据结构与算法 / 霍夫曼树、霍夫曼编码和解码

    一. 诞生原因 找出存放一串字符所需的最少的二进制编码. 二. 构造方法 首先统计出每种字符出现的频率,即:概率.权值. 例如:频率表 A:60,    B:45,   C:13   D:69   E ...

  3. 对文件进行哈夫曼编码压缩与译码的C++实现 以及压缩率计算 ——HIT杨朔

    哈夫曼编码压缩原理:由于每个字符在内存中都是以ASCII码进行存储,所以每个字符都占用了八个01位,利用哈夫曼树对每个字符进行01编码,根据字符在文章中出现的频率调整01串长度,出现频率高的字符在哈夫 ...

  4. c语言用赫夫曼编码压缩文件,用哈夫曼编码C语言实现文件压缩

    用哈夫曼编码实现文件压缩,C语言编写,简单实用, if(j%8!=0) /*按八位读取*/ { for(f=j%8;f<8;f++) strcat(header[i].bits,"0& ...

  5. 用java实现对字符串文本的哈夫曼编码与解码

    哈夫曼树与编码的创建过程及发展由来 这里基础知识就不再叙述了,请参考博客 https://www.cnblogs.com/alomsc/p/12736502.html#:~:text 写的非常详细,初 ...

  6. 哈夫曼编码解压缩文件 - Java实现

    文章目录 前言 一.文件压缩 二.文件解压 结语 前言 不了解哈夫曼树的可以移步查看我的另一篇博客:哈夫曼树(最优二叉树) 使用哈夫曼编码压缩文件,其实就是将每个字符的哈夫曼编码得到,每8位转为一个字 ...

  7. 哈夫曼算法证明+哈夫曼编码译码程序实现

    哈夫曼算法证明 哈夫曼算法是一种贪心算法,我们考虑证明其最优子结构和贪心选择性质: 最优子结构:假设一个树是哈夫曼树,则以其任意节点为根节点的最大子树也是哈夫曼树. 证明:子树的根节点的值是其所有叶子 ...

  8. 【Java数据结构与算法】第十二章 哈夫曼树和哈夫曼编码

    第十二章 哈夫曼树和哈夫曼编码 文章目录 第十二章 哈夫曼树和哈夫曼编码 一.哈夫曼树 1.基本术语 2.构建思路 3.代码实现 三.哈夫曼编码 1.引入 2.介绍 3.代码实现哈夫曼编码综合案例 一 ...

  9. 哈夫曼树与哈夫曼编码:

    定义:带权路径长度WPL最小的二叉树(在编写哈夫曼编码时用到的特殊二叉树) 构造过程: 其实就是每次在权值集合中选两个最小结点的组成新树,然后新树的根节点是二者的权值之和,将刚刚两个从集合中删掉,将新 ...

最新文章

  1. 极客新闻——03、陈庆敏:项目管理的三个关键
  2. centos6.7上使用nginx实现负载均衡!
  3. 【LeetCode 169】Majority Element
  4. centos 下使用sublime
  5. python 反射机制
  6. 为什么私有GIT服务器上无法查看上传的代码?
  7. 利用for...in...遍历js数组与Python异同
  8. 联动椰树花式营销 完成债务重组的瑞幸又“站”了起来
  9. javscript 简单拖拽(drag)拖放事件、dataTransfer详解,垃圾桶效果
  10. 用网页做触摸屏展示的设计要点
  11. python如何压缩pdf_PDF文件怎么压缩,一键压缩PDF文件
  12. .NET Core使用微软官方类库实现汉字简繁切换以及转拼音
  13. 重装linux后没声音,安装虚拟机后没声音了
  14. 第17章 文字和字体
  15. java泡泡屏保,js 模拟气泡屏保效果代码
  16. 笔记本双显卡Ubantu16.04 Nvidia驱动安装指导
  17. 华为云×奇点云 | 828 B2B企业节,一起成就好生意
  18. 分享几个免费的人工智能类API接口
  19. Vue回炉重造之封装防刷新考试倒计时组件
  20. CentOS 7 开机自启汇总

热门文章

  1. 淮阴中学2021高考成绩查询,2021年淮阴高考成绩排名及成绩公布时间什么时候出来...
  2. micro、M3O微服务系列(一)
  3. kali渗透实战02---获取内网QQ相册
  4. Mp4文件播放原理分析
  5. VS2012中--查找定义后从未被使用的函数
  6. 开发必备linux命令大全-稳赚不亏
  7. 2021年12月编程语言排行榜:Python越来越强势
  8. Java一些七七八八的配置
  9. 还没搞懂正则?熬夜到虚脱整理出来的Python的正则表达式总结(Regular Expression)
  10. Matlab论文插图绘制模板第31期—堆叠折线图(stackedplot)