【数据结构 】哈夫曼编译码器

数据结构-----哈夫曼编译码器

  • 题目
    • 题目描述
    • 基本要求
    • 算法分析
  • 代码实现
    • 初始化
    • 编码
    • 解码
    • 打印代码
    • 打印哈夫曼树
  • 总结

题目

题目描述

  • 利用哈夫曼编码进行信息通信可大大提高信道利用率,缩短信息传输时间,降低传输成本。
    要求:在发送端通过一个编码系统对待传数据预先编码;在接收端将传入的数据进行译码(复原)。对于双工信道(即可以双向传输信息的信道),每端都需要个完整的编/译码系统。试为这样的信息收发站写一个哈夫曼的编/译码系统。

基本要求

  • 基本要求
    系统应具有以下功能:
    ① I:初始化.从终端读入字符集大小n及n个字符和n个权值,建立哈夫曼树,井将它存于文件HuffmanTree中。
    ② C:编码。利用已建立好的哈夫曼树(如不在内存,则从文件HuffmanTree中读入)。对文件tobetrans中的正文进行编码,然后将结果存入文件codefile中。
    ③ D:解码。利用已建立好的哈夫曼树将文件codefile中的代码进行译码,结果存入testfile中。
    ④ P:打印代码文件。将文件codefile以紧凑格式显示在终端上,每行50个代码。同时将此字符形式的编码文件写入文件codeprint中。
    ⑤ T:打印哈夫曼树。将已在内存中的哈夫曼树直观的方式(树或凹入表形式)显示在终端上,同时将此字符形式的哈夫曼树写入文件treeprint中。

算法分析

  • 本题例主要用到3个算法如下:
    ① 哈夫曼编码。在初始化(I)的过程中,要用输入的字符和权值建立哈夫曼树并求得哈夫曼编码。先将输入的字符和权值放到一个结构体数据中,建立哈夫曼树,将计算所得的哈夫曼编码存储到另一个结构体数组中。
    ② 串的匹配。在解码(D)的过程中,要对已经编码过的代码进行译码,可利用循环,将代码中与哈夫曼编码长度相同的串与这个哈夫曼编码进行比较,如果相等就回显并存入文件。
    ③二叉树的遍历。在打印哈夫曼树(T)的过程中,因为哈夫曼树也是二叉树,所以就要利用二叉树的前序遍历将哈夫曼树输出。

代码实现

代码部分参考了《数据结构-C语言版第2版》这本书中代码以及C++风格进行书写,测试文件tobetrans中的内容为"ABCABC"

初始化

题目要求从终端读入字符集大小n及n个字符和n个权值,建立哈夫曼树,并将它存于文件HuffmanTree中,可以先将哈夫曼树构建起来,得到哈夫曼树及其对应叶子节点的编码,再通过文件的输入输出将这些编码写入文件HuffmanTree中即可

  • 构造哈夫曼树
/*找出森林集合中根权值最小的两个*/
void Select(HuffmanTree HT,int len,int &s1,int &s2)
{int i,min1=0x3f3f3f3f,min2=0x3f3f3f3f;//先赋予最大值for(i=1;i<=len;i++){if(HT[i].weight<min1&&HT[i].parent==0){min1=HT[i].weight;s1=i;}	}int temp=HT[s1].weight;//将原值存放起来,然后先赋予最大值,防止s1被重复选择HT[s1].weight=0x3f3f3f3f;for(i=1;i<=len;i++){if(HT[i].weight<min2&&HT[i].parent==0){min2=HT[i].weight;s2=i;}}HT[s1].weight=temp;//恢复原来的值
}/*构建哈夫曼树*/
void CreatHuffmanTree(HuffmanTree &HT,int n)
{//构造赫夫曼树HTint m,s1,s2,i;if(n<=1) return;m=2*n-1;HT=new HTNode[m+1];  		//0号单元未用,所以需要动态分配m+1个单元,HT[m]表示根结点   for(i=1;i<=m;++i)        	//将1~m号单元中的双亲、左孩子,右孩子的下标都初始化为0   { HT[i].parent=0;  HT[i].lchild=0;  HT[i].rchild=0; }cout<<"请输入叶子结点的字符和权值:\n";for(i=1;i<=n;++i)        	//输入前n个单元中叶子结点的权值  cin>>HT[i].s>>HT[i].weight;  /*――――――――――初始化工作结束,下面开始创建赫夫曼树――――――――――*/ for(i=n+1;i<=m;++i) {  	//通过n-1次的选择、删除、合并来创建赫夫曼树Select(HT,i-1,s1,s2);//在HT[k](1≤k≤i-1)中选择两个其双亲域为0且权值最小的结点,// 并返回它们在HT中的序号s1和s2HT[s1].parent=i; 	HT[s2].parent=i;   //得到新结点i,从森林中删除s1,s2,将s1和s2的双亲域由0改为iHT[i].lchild=s1;   HT[i].rchild=s2;							//s1,s2分别作为i的左右孩子HT[i].weight=HT[s1].weight+HT[s2].weight; 	//i 的权值为左右孩子权值之和}											
}
  • 哈夫曼编码
/*哈夫曼树编码*/
void CreatHuffmanCode(HuffmanTree HT,HuffmanCode &HC,int n)
{//从叶子到根逆向求每个字符的赫夫曼编码,存储在编码表HC中int i,start,c,f;HC=new char*[n+1];         						//分配n个字符编码的头指针矢量char *cd=new char[n];							//分配临时存放编码的动态数组空间cd[n-1]='\0';                            		//编码结束符for(i=1;i<=n;++i){                      							//逐个字符求赫夫曼编码start=n-1;                          		//start开始时指向最后,即编码结束符位置c=i; f=HT[i].parent;                 			//f指向结点c的双亲结点while(f!=0){                          					//从叶子结点开始向上回溯,直到根结点--start;                          		//回溯一次start向前指一个位置if(HT[f].lchild==c)  cd[start]='0';						//结点c是f的左孩子,则生成代码0else cd[start]='1';                 		//结点c是f的右孩子,则生成代码1c=f; f=HT[f].parent;             			//继续向上回溯}                                  			//求出第i个字符的编码      HC[i]=new char[n-start];         			// 为第i 个字符编码分配空间strcpy(HC[i], &cd[start]);        			//将求得的编码从临时空间cd复制到HC的当前行中}delete cd;                            			//释放临时空间
}
  • 将编码写入到文件HuffmanTree
    这里通过循环将构造好的哈夫曼编码直接写入到文件
//编码写入文件HuffmanTree.txt
void printCreatHuffmanCode(HuffmanTree HT, HuffmanCode HC,int n)
{ofstream hFile("HuffmanTree.txt");if (!hFile.is_open()) {cout << "文件HuffmanTree.txt打开失败." << endl;exit(1);} cout<<"各字符对应的编码如下:"<<endl; for (int i = 1; i <=n; i++){cout << HT[i].s << HC[i] << endl;hFile << HT[i].s << HC[i]<<endl; }hFile.close();cout << "成功写入文件HuffmanTree.txt" << endl;
}

初始化部分运行截图如下:
哈夫曼树及其编码初始化

编码

思路:通过文件逐行读出字符,并将其存放到数组,通过遍历该数组以及哈夫曼树,从而将对应的编码写入到文件codefile.txt中

//编码部分 
void Huffmancode(HuffmanTree HT,HuffmanCode HC,int n) {// 函数用于对 "tobetrans.txt" 文件中的内容进行编码,并将结果存储在 "codefile.txt" 文件中// 假设 "tobetrans.txt" 包含要编码的内容,每一行表示一个要编码的字符串。ifstream tFile("tobetrans.txt");//读 ofstream cFile("codefile.txt");//写 if (!tFile.is_open()) {cout << "文件tobetrans.txt打开失败.\n"<<endl;exit(1);}if (!cFile.is_open()) {cout << "文件codefile.txt打开失败.\n"<<endl;exit(1);}char tobetrans[100];//临时存放文件中每一行的字符 // 逐行读取内容while (tFile.getline(tobetrans, sizeof(tobetrans))) {//行不空 int m= strlen(tobetrans);//数组长度,即每一行字符个数cout<<"tobetrans内容:"<<tobetrans; cout<<endl; for (int i = 0; i <m; i++) {//遍历数组 for(int j=1;j<=n;j++) //遍历树节点(0号单元为根节点未启用) {if (HT[j].s == tobetrans[i]) {//相等则将其对应的编码写入文件 cFile << HC[j];}}}}tFile.close();cFile.close();cout<<"成功写入文件codefile.txt"<<endl;}

编码部分运行截图如下:
将文件中的内容进行编码

解码

思路:和编码部分一样,先从文件逐行读出存入数组,接着再遍历该数组和树的节点,从而将对应的字符写入到testfile.txt中

//译码部分 
void HuffmanDecode(HuffmanTree HT, int n) {//函数用于利用已建立好的哈夫曼树将文件"codefile.txt"中的代码进行译码,结果存入"testfile.txt"中ifstream codeFile("codefile.txt");//读 ofstream testFile("testfile.txt");//写 if (!codeFile.is_open()) {cout << "文件codefile.txt打开失败.\n"<<endl;exit(1);}if (!testFile.is_open()) {cout << "文件testfile.txt打开失败.\n"<<endl;exit(1);}char pwd[100];codeFile.getline(pwd, sizeof(pwd));//每次读取一行放入数组pwd中进行译码 int len = strlen(pwd);int i = 0;int node = 2 * n - 1;//从根节点开始 while (i < len) {  // 循环遍历读取数组中的每个字符while (HT[node].lchild != 0 && HT[node].rchild != 0) {  // 当当前节点不是叶子节点时循环if (pwd[i] == '0') {  // 如果当前读取的字符是 '0'node = HT[node].lchild;  // 移动到当前节点的左孩子节点} else if (pwd[i] == '1') {  // 如果当前读取的字符是 '1'node = HT[node].rchild;  // 移动到当前节点的右孩子节点}i++;  // 移动到下一个字符}testFile << HT[node].s;  // 将叶子节点对应的字符写入到输出文件中node = 2 * n - 1;  // 将节点移动回到根节点}codeFile.close();testFile.close();cout << "成功写入文件testfile.txt" << endl;}

解码部分运行截图如下:
将已经编译好的字符码进行解码

打印代码

思路:通过文件逐行读出二进制码,在根据题目要求将对应的格式输出到终端输入到文件(我这里以每5行为例)

//打印代码文件 
void Print()
{//函数用于将文件codefile.txt以紧凑格式显示在终端上,每行5个代码。//同时将此字符形式的编码文件写入文件codeprint.txt中。 char str[1000];//存储codefile.txt的0-1编码 int num = 0;ifstream file1("codefile.txt");//读出 ofstream file2("codeprint.txt");//写入 if(file1.fail()){cout<<"文件codefile.txt打开失败"<<endl;exit(0);}if(file2.fail()){cout<<"文件codeprint.txt打开失败"<<endl;exit(0);}file1.getline(str, 10000);//读取每一行cout<<"str内容"<<str; cout<<endl;cout<<"文件codefile.txt内容显示如下:"<<endl; while(str[num]){if(num%5==0&&num!=0)//每输出5个字符换行,由于数组下标从0开始所以要忽略num==0这个情况 {cout<<endl; file2<<endl;//文件里也是每5个字符换行 }cout<<str[num];file2<<str[num];//写入文件 num++; 				}cout<<endl;file1.close();file2.close();cout<<"成功写入文件codeprint.txt"<<endl;	}

打印代码部分运行截图如下:
打印代码文件

打印哈夫曼树

这里结合树的前序遍历,并以凹入表的形式进行输出

void PrintHuffmanTree(HuffmanTree HT, int root, int level) {// 递归打印哈夫曼树的结构(凹入表形式)if (root != 0) {// 打开文件 treeprint.txtofstream treeFile("treeprint.txt", ios::app);  // 使用 ios::app 追加模式if (treeFile.fail()) {cout << "文件 treeprint.txt 打开失败" << endl;exit(0);}// 输出当前节点信息(以凹入表形式)cout << string(4 * level, ' ');  // 控制缩进cout << HT[root].weight;//输出节点权值 if (HT[root].s) {cout << " (" << HT[root].s << ")";//节点字符 }cout << endl;treeFile << string(4 * level, ' ');treeFile << HT[root].weight;if (HT[root].s) {treeFile << " (" << HT[root].s << ")";}treeFile << endl;// 递归打印左右子树PrintHuffmanTree(HT, HT[root].lchild, level + 1);PrintHuffmanTree(HT, HT[root].rchild, level + 1);}
}

打印哈夫曼树运行截图如下:
打印哈夫曼树

总结

关于如何书写一个哈夫曼编译码器,以上代码大家可以参考,仅供参考!!!毕竟我写的也不是很优秀,只能做到题目的要求,代码中还有许多可以优化的地方,介于自己能力有限,就先分享这么多,大家如果在参考以上代码存在问题时,需要我自己书写的完整代码可以私信我哦,在此感谢各位大佬的支持!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/623660.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CRM的销售管道是什么?有哪些应用价值?

常常阅读CRM管理系统相关内容的朋友会发觉&#xff0c;这些文章中总会提及一个词——【销售管道】。销售管道是一种重要的销售工具&#xff0c;是营销过程的可视化表达。销售管道清晰地叙述了业务状态&#xff0c;例如商机处在哪个阶段/哪些是更高效的销售活动。销售管道为企业…

操作系统(复习提纲)

现在距离操作系统考试还剩三天&#xff0c;我今天刚刚整理好这份提纲&#xff0c;里面还附加了一些可能考的计算题的讲解视频&#xff0c;都是B站上一些优秀的UP主录制的&#xff0c;我觉得讲的还挺好的&#xff0c;对于应付考试&#xff0c;以不挂科为宗旨应该可以哈哈哈。 1…

爬虫案例—抓取豆瓣电影的电影名称、评分、简介、评价人数

爬虫案例—抓取豆瓣电影的电影名称、评分、简介、评价人数 豆瓣电影网址&#xff1a;https://movie.douban.com/top250 主页截图和要抓取的内容如下图&#xff1a; 分析&#xff1a; 第一页的网址&#xff1a;https://movie.douban.com/top250?start0&filter 第二页的…

五、程序流程结构(2)循环结构——while语句

作用&#xff1a; 满足循环条件&#xff0c;执行循环语句 语法&#xff1a; while(循环条件){循环语句}; 解释&#xff1a;只要循环条件的结果为真&#xff0c;就执行循环语句 1、循环打印0-9 #define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<strin…

Cypress安装与使用教程(4)—— 软测大玩家

&#x1f60f;作者简介&#xff1a;博主是一位测试管理者&#xff0c;同时也是一名对外企业兼职讲师。 &#x1f4e1;主页地址&#xff1a;【Austin_zhai】 &#x1f646;目的与景愿&#xff1a;旨在于能帮助更多的测试行业人员提升软硬技能&#xff0c;分享行业相关最新信息。…

Java实现农村物流配送系统 JAVA+Vue+SpringBoot+MySQL

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统登录、注册界面2.2 系统功能2.2.1 快递信息管理&#xff1a;2.2.2 位置信息管理&#xff1a;2.2.3 配送人员分配&#xff1a;2.2.4 路线规划&#xff1a;2.2.5 个人中心&#xff1a;2.2.6 退换快递处理&#xff1a;…

Java调用openai微调Fine-tuning实战示例

注: 建议先看微调文档, 遵从官网给出的规则。例如: jsonl训练文件至少有10个例子, 否则报错 官网微调文档https://platform.openai.com/docs/guides/fine-tuning官网微调APIhttps://platform.openai.com/docs/api-reference/fine-tuning 1. 实现步骤 1. 准备好jsonl数据集2…

当前vscode环境下 多进程多线程运行情况探究

我的代码 其中在“打开图片时”、“进入子进程之前”、“子进程join前”、“进入子进程区域后”&#xff0c;“子进程join后”、“进入子线程区域后”分别打印了进程线程的编号和数量。 # -*- coding: utf-8 -*-# Form implementation generated from reading ui file test2.…

好书推荐丨AI时代Python量化交易实战:ChatGPT让量化交易插上翅膀(北大社)

文章目录 写在前面关键点内容简介作者简介推荐理由粉丝福利写在后面 写在前面 本期博主给大家推荐一本Python量化交易实战类书籍&#xff1a; ChatGPT让量化交易师率飞起来&#xff01; 金融量化交易新模式一本专注于帮助交易师在AI时代实现晋级、提高效率的图书书中介绍了如…

【Rust日报】2024-01-12 将 Rust 引入 Git 项目

将 Rust 引入 Git 项目 去年年底的假期里&#xff0c;Taylor Blau 花了一些时间思考如何将 Rust 引入 Git 项目。 将 Rust 引入 Linux 内核的重要工作正在进行中。在他们既定的目标中&#xff0c;他认为有一些可能与 Git 项目相关&#xff1a; 由于语言的安全保证&#xff0c;内…

ssm基于vue的儿童教育网站的设计与实现论文

摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;视频信息因为其管理内容繁杂&#xff0c;管理数量繁多导致手工进行处理不能满足广大…

开启C++之旅(上):探索命名空间与函数特性(缺省参数和函数重载)

之前浅显的讲解了数据结构的部分内容&#xff1a;数据结构专栏 那么今天我们迎来了新的起点&#xff1a;C的探索之旅 文章目录 1.命名空间1.1引入命名冲突1.2命名空间1.2.1命名空间的定义1.2.2命名空间的使用 2.c的输入与输出3.缺省参数3.1概念3.2缺省参数分类 4.函数重载4.1概…

【计算机二级考试C语言】C变量

C 变量 变量其实只不过是程序可操作的存储区的名称。C 中每个变量都有特定的类型&#xff0c;类型决定了变量存储的大小和布局&#xff0c;该范围内的值都可以存储在内存中&#xff0c;运算符可应用于变量上。 变量的名称可以由字母、数字和下划线字符组成。它必须以字母或下…

12-桥接模式(Bridge)

意图 将抽象部分与它的实现部分分离&#xff0c;使他们可以独立地变化 个人理解 一句话概括就是只要是在抽象类中聚合了某个接口或者抽象类&#xff0c;就是使用了桥接模式。 抽象类A中聚合了抽象类B&#xff08;或者接口B&#xff09;&#xff0c;A的子类的方法中在相同的场…

基于elementUI的el-table组件实现按住某一行数据上下滑动选中/选择或取消选中/选择鼠标经过的行

实现代码 <template><div :class"$options.name"><el-tablestyle"user-select: none"ref"table":data"tableData":row-class-name"row_class_name"mousedown.native"mousedownTable"row-click&q…

[力扣 Hot100]Day5 盛最多水的容器

题目描述 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 说明&#xff1a;你不能倾斜容…

2、BERT:自然语言处理的变革者

请参考之前写的&#xff1a;2、什么是BERT&#xff1f;-CSDN博客文章浏览阅读826次&#xff0c;点赞19次&#xff0c;收藏22次。BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;是Google在2018年提出的一种自然语言处理&#xff08;NLP&…

linux安装QQ(官方正版)

QQ官网上有支持linux系统的版本&#xff0c;所以去官网直接下载正版就好。 安装步骤&#xff1a; 1.进入官网&#xff1a;https://im.qq.com/linuxqq/index.shtml 2.选择版本&#xff1a;X86版下载dep 如下所示&#xff1a; 3.下载qq安装包&#xff1a; 4.使用命令安装qq s…

Android 事件分发机制详解(下)

2.3 View事件分发机制 从上面 ViewGroup 事件分发机制知道&#xff0c;View事件分发机制从 dispatchTouchEvent() 开始 源码分析 /*** 源码分析&#xff1a;View.dispatchTouchEvent&#xff08;&#xff09;*/public boolean dispatchTouchEvent(MotionEvent event) { if …

国科大-自然语言处理复习

自然语言处理复习 实体关系联合抽取流水线式端到端方法 检索式问答系统流水线方式信息检索&#xff08;IR&#xff09;阶段阅读理解&#xff08;RC&#xff09;阶段基于证据强度的重排基于证据覆盖的重排结合不同类型的聚合 端到端方式Retriever-Reader的联合学习基于预训练的R…