一.Huffman编码与解码 (Huffman编码、二叉树)
[问题描述]
对一篇英文文章(大于2000个英文字符),统计各字符出现的次数,实现Huffman编码,以及对编码结果的解码。
[基本要求]
(1) 输出每个字符出现的次数和编码,其中求最小权值要求用堆实现。
(2) 在Huffman编码后,要将编码表和英文文章编码结果保存到文件中,编码结果必须是二进制形式,即0 1的信息用比特位表示,不能用字符’0’和’1’表示。
(3) 提供读编码文件生成原文件的功能。

二.数据结构说明
在该程序中使用了两个个结构体来完成编码,用位域来实现bite流存储,字符的种类是根据ascll码表里的字符对应匹配存储的:

const charsourcef = “d:\Cpp-charcode\EnglishSC.txt”;//英文源文章
const char
transf = “d:\Cpp-charcode\EnglishT.txt”;//通过编码翻译的英文文章
const charsortf = “d:\Cpp-charcode\EnglishST.txt”;//分类好的字符及频数编码
const char
codef = “d:\Cpp-charcode\EnglishCode.dat”;//整篇英文文章的编码
const char*bitef = “d:\Cpp-charcode\EnglishBite.dat”; //每一种字符的比特位编码
注意:这里的路径,其中文件夹是要事先手动创建的。

//哈夫曼树
typedef struct HTNode
{
int Ascll;//字符对应的ASCLL码
int weight;//权值
int LC, RC, P;//左,右,父结点下标
}HTNode,*LinkHTNode;

//存储叶子节点编码在哈夫曼树对应的位置及,编码
typedef struct HTStr
{
int pos;//叶子节点的字符在哈夫曼树中的位置
int s;//ASCLL码值
char *code;//字符对应的哈夫曼编码
}*LinkHTStr;

//位域bite
typedef struct Bite
{
unsigned int bit : 1;//占一个位的无符号整形
}Bite;

三、执行结果

原英文文章

哈夫曼树结构(子结点和父结点的情况):
ASCLL为0表示为内节点,并非叶子节点



字符统计情况:


整个文章的哈夫曼码:


通过哈夫曼树解码出的结果:

四、代码情况
所需头文件&结构定义:

#include "stdafx.h"
#include<iostream>
#include<fstream>
#include<iomanip>
using namespace std;const char*sourcef = "d:\\Cpp-charcode\\EnglishSC.txt";//英文源文章
const char*transf = "d:\\Cpp-charcode\\EnglishT.txt";//通过编码翻译的英文文章
const char*sortf = "d:\\Cpp-charcode\\EnglishST.txt";//分类好的字符及频数编码
const char*codef = "d:\\Cpp-charcode\\EnglishCode.dat";//整篇英文文章的编码
const char*bitef = "d:\\Cpp-charcode\\EnglishBite.txt"; //每一种字符的比特位编码//哈夫曼树
typedef struct HTNode
{int Ascll;//字符对应的ASCLL码int weight;//权值int LC, RC, P;//左,右,父结点下标
}HTNode,*LinkHTNode;//存储叶子节点编码在哈夫曼树对应的位置及,编码
typedef struct HTStr
{int pos;//叶子节点的字符在哈夫曼树中的位置int s;//ASCLL码值char *code;//字符对应的哈夫曼编码
}*LinkHTStr;//位域bite
typedef struct Bite
{unsigned int bit : 1;//占一个位的无符号整形
}Bite;

PS:值得再次强调,文件路径里的文件夹需要自己手动创建好,程序执行过程是不会创建文件夹的。

函数定义&实现:

//分类函数
void sort_str(const char*file1,LinkHTNode &HT)
{char ch;//临时存储每次拿到的字符int s;//用于转换ASCLL码对应的十进制数值int *strw = new int[100];//临时存储英文文章统计后的每种字符的个数//初始化统计字符频数数组for (int i = 0; i < 100; i++){strw[i] = 0;}// 分类文件字符ifstream ifile(file1, ios::in);while (ifile.get(ch)){s = ch;if (s == 10) { strw[0]++; }//换行符if ((s - 31)>0) { strw[s - 31]++; }//32-127号的字符}//统计出现的字符种类s = 0;for (int i = 0; i < 100; i++){if (strw[i] != 0)s++;}//初始化哈夫曼树HT = new HTNode[2 * s ];//0号位不存,需要多开一个单元内存for (int i = 0; i < 2 * s ; i++){HT[i].Ascll = 0; HT[i].weight = 0;HT[i].LC = HT[i].RC = HT[i].P = 0; }HT[0].weight = s;//叶子结点数//剔除未出现的字符,存储字符和权值int  k;if (strw[0] != 0)//换行符单独判断存储{HT[1].Ascll = 10;HT[1].weight = strw[0];HT[0].Ascll += HT[1].weight;k = 2;//有换行符就从2位置开始存储之后的字符}else { k = 1; }//否则,从1号位开始for (int i=1;i <100; i++){if (strw[i] != 0){HT[k].Ascll = i + 31;HT[k].weight = strw[i];HT[0].Ascll += HT[k].weight;k++;}}delete []strw;//销毁临时空间
}//初建堆
void HeapAdjust(LinkHTNode &HT, int s, int m)//哈夫曼树,堆头,堆尾
{HTNode rc = HT[s];//暂存堆顶结点for (int j = 2 * s; j <= m; j *= 2){if (j < m&&HT[j].weight > HT[j + 1].weight)++j;//左右孩子比较大小,取小的一个if (rc.weight <= HT[j].weight)break;HT[s] = HT[j]; s = j;//较小的孩子与父节点比较,小的最后作为的父节点}HT[s] = rc;
}
void CreatHeap(LinkHTNode &HT)
{int n = HT[0].weight;//初始堆的个数,即叶子节点数for (int i = n / 2; i > 0; --i)HeapAdjust(HT, i, n);
}//堆排序
void HeapSort(LinkHTNode &HT,LinkHTStr &Str)//哈夫曼树,叶子结点表
{CreatHeap(HT);Str = new HTStr[HT[0].weight];//动态建立Strint n = HT[0].weight;//叶子结点数int k = 0;int i = 2 * n - 1;//哈夫曼树表尾//创建哈夫曼树for ( ;i >1 ;i--){k++;HT[i]= HT[1];//选出的堆顶元素放到哈夫曼树尾if (k % 2) { HT[1] = HT[n]; n--;  }//只选出一个最小数时,堆尾放上上对顶else //选出两个最小数之后,合成新的节点,合成新的结点放在堆顶{ HT[1].weight = HT[i + 1].weight + HT[i].weight; HT[1].Ascll = 0; HT[1].LC = i + 1;HT[1].RC = i;}HeapAdjust(HT, 1, n);//堆排}//挑选叶子节点储存到另一个空间n--;for (; i <= k + 1; i++){if (HT[i].LC) { HT[HT[i].LC].P = i; }if (HT[i].RC) { HT[HT[i].RC].P = i; }if (HT[i].Ascll) { Str[n].pos = i; Str[n].s = HT[i].Ascll; n++; }}
}//哈夫曼编码
void HTFmanCode(LinkHTNode &HT,LinkHTStr &Str)
{char *code = new char[HT[0].weight];//临时的存储每一个字符的编码int n = HT[0].weight-1;int f, c;//f为父结点,c为子结点for (int i = 0; i < HT[0].weight; i++){f = HT[Str[i].pos].P;//父节点c = Str[i].pos;//子节点while (f){if (c == HT[f].LC) { code[n] = '0'; }else { code[n] = '1'; }n--;c = f;//往根的方向回溯,子结点更改为它的父结点f = HT[f].P;}//整理每次编好的字符编码,存储到Str[i].code中int length = HT[0].weight - n - 1;Str[i].code = new char[length+1];Str[i].code[length] = '\0';for(int k=0;k<length;k++){Str[i].code[k] = code[n + 1];n++;}}
}// 存储
void Save(const char*filechar, const char*filecode, LinkHTStr Str,LinkHTNode HT)
{ofstream ofilechar(filechar, ios::out);//储存字符,权值,编码的文件ofstream ofilecode(filecode, ios::binary);//存储整篇文章的编码的文件ofstream ofilebite(bitef, ios::binary);//存储每种字符的编码文件ifstream ifile(sourcef, ios::out);char ch; Bite B_Save;//储存字符,权值,编码到文件ofilechar << "字符" << setw(20) << "频数" << setw(20) << "编码" << endl;for (int i = 0; i < HT[0].weight; i++){if (Str[i].s == 10) { ofilechar << "LF" << setw(20) << HT[Str[i].pos].weight << setw(22) << Str[i].code << endl; continue; }if (Str[i].s == 32) { ofilechar << "Space" << setw(17) << HT[Str[i].pos].weight << setw(22) << Str[i].code << endl; continue; }ofilechar << char(Str[i].s) << setw(21) << HT[Str[i].pos].weight << setw(22) << Str[i].code << endl;//下面的for用于存储编码的比特位(每种字符的)for (int k = 0; Str[i].code[k] != '\0'; k++){if (Str[i].code[k] == '0')B_Save.bit = 0;else B_Save.bit = 1;ofilebite << B_Save.bit;}}//存储整篇文章的编码while (ifile.get(ch)){for (int i = 0; i < HT[0].weight; i++){if (ch == char(Str[i].s)){ofilecode << Str[i].code;}}}//关闭文件ofilechar.close();ofilecode.close();ofilebite.close();ifile.close();}
//通过01文件编码翻译文章
void Translate(const char*filecode, const char*fileTrans,LinkHTNode HT)
{ifstream ifile(filecode, ios::in);ofstream ofile(fileTrans, ios::out);char ch;int f=1;while (ifile.get(ch)){if (ch == '0')f = HT[f].LC;else f = HT[f].RC;if (HT[f].LC == 0&&HT[f].RC == 0) { ofile << char(HT[f].Ascll); f = 1; continue; }}ifile.close();ofile.close();
}

主函数:

int main()
{HTNode *HT;HTStr *Str;sort_str(sourcef,HT);HeapSort(HT, Str);HTFmanCode(HT, Str);Save(sortf, codef, Str, HT);Translate(codef, transf, HT);cout << "字符总个数:  " << HT[0].Ascll <<'\n'<< endl;cout << "字符总类:   " << HT[0].weight << '\n' << endl;cout << "哈夫曼树创建成功\n" << endl;cout << "所有字符以及、字符对应的哈夫曼编码已保存到相应的txt文件\n"<<endl;ofstream of(bitef, ios::out);of << "ASCLL码" << setw(10) << "位置" << setw(10) << "左孩子" << setw(10) << "右孩子" << setw(10)<<"父结点"<<endl;for (int i = 1; i < 2 * HT[0].weight; i++){of << HT[i].Ascll << setw(10) << i << setw(10) << HT[i].LC << setw(10) << HT[i].RC << setw(10) << HT[i].P << endl;}of.close();return 0;}

五、小结

  1. 分类函数是把原文章的字符分好类,并统计好出现的频数,并把出现的次数当作其权值大小。
  2. 在这里用了堆排序进行筛选每次新增结点和原始节点中的最大权值。

哈夫曼编码--英语文章的编码译码(c/c++)相关推荐

  1. 哈夫曼树的构建、编码以及带权路径长计算

    给定n个权值作为n个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree).哈夫曼树是带权路径长度最短的树,权值较大的结点离根较 ...

  2. 哈夫曼树原理及Java编码实现

    文章目录 前言 一.哈夫曼树原理 二.哈夫曼编码(Java题解) 参考资料 前言 所有博客文件目录索引:博客目录索引(持续更新) 源代码:Gitee-Huffman.java.Github-Huffm ...

  3. 【id:179】【20分】C. DS二叉树--赫夫曼树的构建与编码(不含代码框架)

    题目描述 给定n个权值,根据这些权值构造huffman树,并进行huffman编码 参考课本P147算法6.12 HuffmanCoding代码,注意数组访问是从位置1开始 要求:赫夫曼的构建中,默认 ...

  4. c语言最优树的构造,哈夫曼树的构造及编码 Haffman树的构造及其编码

    写出构造完整的哈夫曼树的编码 void HuffmanCoding(HuffmanCode HC[], int w[], int n) // w存放n个字符的权值(均>0),构造哈夫曼树HT, ...

  5. 信息论与编码_哈夫曼编码

    哈夫曼树 哈夫曼树(Huffman Tree)也是一种特殊的二叉树,这种树的所有叶子结点都带有权值,从中构造出带权路径长度最短的二叉树,即哈夫曼树. 哈夫曼树的定义 ​ 设二叉树具有n个带权值的叶子结 ...

  6. [题解] 哈夫曼编码(附图分析)

    "Don't bark up the wrong Binary Tree." [问题描述] 我们称树的带权路径长度(WPL)最小的二叉树为"哈夫曼树"或&quo ...

  7. 文件的哈夫曼编码与解码

    文件的哈夫曼编码与解码 编码过程中,踩了一些小坑,做下记录: 1.全局变量count与std:count矛盾,建议用其他变量名. 2.内存泄漏问题 注意空间要开够 指针不可越界 main函数内开辟的栈 ...

  8. 使用Java实现哈夫曼编码(Huffman Coding)

    文章目录 (一)需求分析 (二)构建哈夫曼树 (三)构建哈夫曼编码 (四)哈夫曼编码的解码 (五)哈夫曼编码压缩的原理 (六)总结 (七)Java代码实现哈夫曼树:构建节点类&二叉树类 (八) ...

  9. 5.1 Python图像处理之图像编码-哈夫曼编码

    5.1 Python图像处理之图像编码-哈夫曼编码 文章目录 5.1 Python图像处理之图像编码-哈夫曼编码 1 算法原理 2 代码 3 效果 1 算法原理 哈夫曼编码是一种根据词频变化的变长二进 ...

最新文章

  1. 中国水处理行业十四五趋势规划与布局动态分析报告2022年
  2. 关于8位AD_DA转换芯片的采样率问题
  3. neditor 自定义工具栏配置
  4. bert pytorch 序列标注_序列标注:Bi-LSTM + CRF
  5. 3-39客户端(client)写数据到HDFS的流程
  6. word目录怎么跳转到相应页码_Word目录不会做?请看完整操作步骤
  7. TCP粘包拆包基本解决方案
  8. 【numpy学习】numpy教程--基于莫烦python的教程
  9. 推荐给每个找工作的IT毕业生--打鸡血书
  10. journalctl -xe
  11. 忍无可忍,决定宁可错杀千万,也不漏掉一个,弹已出膛,剑已出鞘!
  12. Git、Github、Gitee、GitLab学习笔记
  13. Python语言程序设计基础 第二版(嵩天著)课后答案第六章
  14. 每日新闻丨雷军:金山办公分拆上市是既定战略;我国计划2022年前后建成可载3人的空间站...
  15. java 命令 线程栈_JVM调试常用命令——jstack命令与Java线程栈(1)
  16. P quant与Q quant
  17. 一场网约车司机们的「合作社实验」
  18. java打印图片_java如何调用本地打印机进行图片打印
  19. NFC读写MifareClassic协议的NFC卡
  20. 广东省计算机水平考试证书有效期,软件设计师证书有效期是多久?永久有效吗?...

热门文章

  1. Vmware上安装Vxworks 5.5
  2. linux少了 dev dm设备,已解决: Linux中安装了powerpath之后为什么还会有dm设备? - Dell Community...
  3. 巴黎欧莱雅沙龙专属全球首家旗舰沙龙于上海开业
  4. LearningSpark(5):Spark共享变量理解
  5. 常规卷积,DW卷积和PW卷积的区别
  6. 加权无向图与最小生成树(Prim算法和Kruskal算法)
  7. LCD显示屏-基础篇(屏的角度)
  8. 从零开始玩rv1126core板(零)
  9. 实习日记 7.10
  10. 蒂娜交易准则(黄金高胜算交易)