C++哈夫曼树+哈夫曼编码的实现(双完整版)

2024-06-21 17:38

本文主要是介绍C++哈夫曼树+哈夫曼编码的实现(双完整版),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

注释详解哈夫曼Tree和哈夫曼Code

  • 一、哈夫曼Tree
  • 二、哈夫曼Code

  本文是根据B站视频👉青岛大学 - 王卓老师的数据结构来实现的,涉及到哈夫曼Tree 和 哈夫曼Code的C++版完整实现,若有不足欢迎大佬斧正-(/▽\)

一、哈夫曼Tree

  具体理论请配合👉B站视频来学习,构造哈夫曼Tree主要的方法如下:
  第一步:构造森林全是根
  第二步:选用两小造新树
  第三步:删除两小添新人(parent设置为 n+1 到 2n-1 中的下标)
  第四步:重复2、3步剩单根
  话不多说,我们只需要记住这四步,把下面代码的框架敲熟了,就能运用自如了。
  PS:代码有详细注解 和 引导思考,喜欢的话可以收藏一波~

#include <iostream>
#include <algorithm>
#include <iomanip>
using namespace std;typedef struct HFT
{int weight;int parent, LTree, RTree;
}HFT, *PHFT;void Select(PHFT HFT, int n, int &s1, int &s2)    //选取权值最小的两个结点
{for(int i = 1; i < n; i++){         //初始化s1,s1的双亲为0if(HFT[i].parent == 0){     /**  思考:为什么不是 i <= n  **/s1 = i;					//解答:因为我们第 2n-1  个元素的parent是0break;                          /**  思考:为什么要加上 break  **/}}for(int i = 1; i < n; i++){         //s1为权值最小的下标if(HFT[i].parent == 0 && HFT[s1].weight > HFT[i].weight)s1 = i;}for(int j = 1; j < n; j++){         //初始化s2,s2的双亲为0if(HFT[j].parent == 0 && j != s1){s2 = j;break;}}for(int j = 1; j < n; j++){         //s2为另一个权值最小的下标if(HFT[j].parent == 0 && HFT[s2].weight > HFT[j].weight && j != s1)s2 = j;}
}void initHFT(PHFT &H, int n)
{if(n <= 1) return;int m = 2*n - 1;            //数组共2n - 1个元素H = new HFT[m + 1];	//0号单元未用,H[m]表示根节点for(int i = 1; i <= m; i++){H[i].parent = 0;H[i].LTree = 0;H[i].RTree = 0;}cout << "please input the weight of nodes:" << endl;for(int i = 1; i <= n; i++)cin >> H[i].weight;cout << endl;for(int i = n + 1; i <= m; i++)         //产生的新结点要放在从n+1开始,一直到2n-1的位置{int s1, s2;Select(H, i, s1, s2);H[s1].parent = i;H[s2].parent = i;   //相当于从表F中删除s1, s2H[i].LTree = s1;H[i].RTree = s2;H[i].weight = H[s1].weight + H[s2].weight;}
}void showHFT(PHFT &H, int n)
{cout << "index  weight  parent  LTree  RTree" << endl;cout << left;     //左对齐输出int m = 2*n - 1;for(int i = 1; i <= m; i++){cout << setw(5) << i << "  ";                       /** 思考: 为什么是setw(5) 和 setw(6)   **/cout << setw(6) << H[i].weight << "  ";     // 解答:当后面紧跟着的输出字段长度小于n的时候,在该字段前面用空格补齐;当输出字段长度大于n时,全部整体输出cout << setw(6) << H[i].parent << "  ";cout << setw(6) << H[i].LTree << "  ";cout << setw(6) << H[i].RTree << "  " << endl;}
}int main()
{PHFT HFT;int n = 0;cout << "please input the number of nodes: ";cin >> n;initHFT(HFT, n);showHFT(HFT, n);system("pause");return 0;
}

  我们可以对应下面这张图来看代码,弄懂思路。
在这里插入图片描述
Input
please input the number of nodes: 7
please input the weight of nodes:
7
19
2
6
32
3
21

Output
index weight parent LTree RTree
1  7   0   0   0
2  19  11   0   0
3  2   8   0   0
4  6   9   0   0
5  32  12   0   0
6  3   8   0   0
7  21  12   0   0
8  5   9   3   6
9  11  10   8   4
10  18  11  1   9
11  37  13  10   2
12  53  13  7   5
13  90  0  11   12
  

二、哈夫曼Code

  该代码在实现哈夫曼编码核心算法时既使用了C++的string类来实现,也使用了C的方式实现。这是在学会构造哈夫曼树之后的进一步提升,在这里给需要提高的同学抛出一个思考问题,“C++如何处理模板类template实现自动根据用户输入的 weight 值类型来分配内存”。
在这里插入图片描述
  首先先看图,根据图来实现以下步骤(用char动态数组):
  第一步:构建哈夫曼树表、HC表(动态二维数组)、cd表(一维)
  第二步:一般规定左子树路径为0,右子树路径为1,按哈夫曼树表寻找parent结点直到为0。
  与第二步同时进行:先将临时cd表最后一个元素定为’\0’,创建临时结点记录当前处理的结点。
  第三步:将cd表值赋值给HC表,同时销毁cd表的临时内存。

在这里插入代码片```cpp
#include <iostream>
#include <string.h>
#include <iomanip>
#include <stack>
#include <algorithm>using namespace std;typedef struct HFT
{float weight;int parent, LTree, RTree;string name;
}HFT, *PHFT;void Select(const PHFT& H, const int& n, int& s1, int& s2){for(int i = 1; i < n; i++){if(H[i].parent == 0){s1 = i;break;}}for(int i = 1; i < n; i++){if(H[i].parent == 0 && H[s1].weight > H[i].weight)s1 = i;}for(int j = 1; j < n; j++){if(H[j].parent == 0 && j != s1){s2 = j;break;}}for(int j = 1; j < n; j++){if(H[j].parent == 0 && H[s2].weight > H[j].weight && j != s1)s2 = j;}
}void initHFC(PHFT& HT, const int& n)
{if(n <= 1) return;int m = 2*n - 1;HT = new HFT[m + 1];for(int i = 1; i <= m; i++){HT[i].parent = 0;HT[i].LTree = 0;HT[i].RTree = 0;}cout << "please input the weight of nodes and nodes' name as 0.23 A: " << endl;for(int i = 1; i <= n; i++){cin >> HT[i].weight >> HT[i].name;}cout << endl;for(int i = n+1; i <= m; i++){int s1, s2;Select(HT, i, s1, s2);HT[s1].parent = i;HT[s2].parent = i;HT[i].LTree = s1;HT[i].RTree = s2;HT[i].weight = HT[s1].weight + HT[s2].weight;}
}/**      char 类型解决方案   */
void ch_CreateHFMcode(const PHFT& HT, char** HC, const int& n)
{char* temp = new char[n];temp[n-1] = '\0';int  start = 0, c = 0, father = 0;for(int i = 1; i <= n; i++){start = n - 1;c = i;                      //记录正在处理的当前位置father = HT[i].parent;while(father != 0)   //从叶子节点向上回溯{                //回溯一次 start指向前一个位置一个if(HT[father].LTree == c) temp[--start] = '0';else temp[--start] = '1';c = father;             //当前位置移到父节点father = HT[father].parent;    //更新父节点,继续向上回溯}HC[i] = new char[n-start];            // 为第 i 个字符串编码分配空间strcpy(HC[i], &temp[start]);             // 将求得的编码从临时空间cd复制到HC的当前行中,strcpy遇到 \0 拷贝就会结束}delete temp;
}/**   string 类型的解决方案   **/
void str_CreateHFMcode(PHFT& H, string *HC, const int& n)
{string temp;stack<string> st;		//利用栈实现上一个char类型strcpy的方法int cur = 0, father = 0;for(int i = 1; i <= n; i++){cur = i;father = H[i].parent;while(father != 0){if(H[father].LTree == cur) st.push("0");else st.push("1");cur = father;father = H[father].parent;}while(!st.empty()){temp += st.top();st.pop();}HC[i] = temp;temp.erase();	//擦除内存}
}void showdata(const PHFT& HFT, char** HC, const int& n){cout << "index  weight  parent  LTree  RTree" << endl;cout << left;int m = 2*n - 1;for(int i = 1; i <= m; i++){cout << setw(5) << i << "  ";cout << setw(6) << HFT[i].weight << "  ";cout << setw(6) << HFT[i].parent << "  ";cout << setw(6) << HFT[i].LTree << "  ";cout << setw(6) << HFT[i].RTree << "  " << endl;}cout << endl;cout << "Name  HFMCode" << endl;for(int i = 1; i <= n; i++){cout << setw(5) << HFT[i].name << "  ";cout << setw(7) << HC[i] << "  " << endl;}
}int main()
{PHFT HFT;int n = 0;cout << "please intput the number of vertices: ";cin >> n;char** HC = new char*[n];initHFC(HFT, n);ch_CreateHFMcode(HFT, HC, n);showdata(HFT, HC, n);return 0;
}

Input
please intput the number of vertice: 7
what value do you want to give them?
0.4
0.3
0.15
0.05
0.04
0.03
0.03

Output
index HC[i]
1  0
2  10
3  110
4  11111
5  11110
6  11100
7  11101
  
路曼曼其修远兮,吾将上下而求索

这篇关于C++哈夫曼树+哈夫曼编码的实现(双完整版)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1081891

相关文章

Spring Security+JWT如何实现前后端分离权限控制

《SpringSecurity+JWT如何实现前后端分离权限控制》本篇将手把手教你用SpringSecurity+JWT搭建一套完整的登录认证与权限控制体系,具有很好的参考价值,希望对大家... 目录Spring Security+JWT实现前后端分离权限控制实战一、为什么要用 JWT?二、JWT 基本结构

Java实现优雅日期处理的方案详解

《Java实现优雅日期处理的方案详解》在我们的日常工作中,需要经常处理各种格式,各种类似的的日期或者时间,下面我们就来看看如何使用java处理这样的日期问题吧,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言一、日期的坑1.1 日期格式化陷阱1.2 时区转换二、优雅方案的进阶之路2.1 线程安全重构2

Android实现两台手机屏幕共享和远程控制功能

《Android实现两台手机屏幕共享和远程控制功能》在远程协助、在线教学、技术支持等多种场景下,实时获得另一部移动设备的屏幕画面,并对其进行操作,具有极高的应用价值,本项目旨在实现两台Android手... 目录一、项目概述二、相关知识2.1 MediaProjection API2.2 Socket 网络

使用Python实现图像LBP特征提取的操作方法

《使用Python实现图像LBP特征提取的操作方法》LBP特征叫做局部二值模式,常用于纹理特征提取,并在纹理分类中具有较强的区分能力,本文给大家介绍了如何使用Python实现图像LBP特征提取的操作方... 目录一、LBP特征介绍二、LBP特征描述三、一些改进版本的LBP1.圆形LBP算子2.旋转不变的LB

Redis消息队列实现异步秒杀功能

《Redis消息队列实现异步秒杀功能》在高并发场景下,为了提高秒杀业务的性能,可将部分工作交给Redis处理,并通过异步方式执行,Redis提供了多种数据结构来实现消息队列,总结三种,本文详细介绍Re... 目录1 Redis消息队列1.1 List 结构1.2 Pub/Sub 模式1.3 Stream 结

C# Where 泛型约束的实现

《C#Where泛型约束的实现》本文主要介绍了C#Where泛型约束的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录使用的对象约束分类where T : structwhere T : classwhere T : ne

将Java程序打包成EXE文件的实现方式

《将Java程序打包成EXE文件的实现方式》:本文主要介绍将Java程序打包成EXE文件的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录如何将Java程序编程打包成EXE文件1.准备Java程序2.生成JAR包3.选择并安装打包工具4.配置Launch4

MySQL索引的优化之LIKE模糊查询功能实现

《MySQL索引的优化之LIKE模糊查询功能实现》:本文主要介绍MySQL索引的优化之LIKE模糊查询功能实现,本文通过示例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录一、前缀匹配优化二、后缀匹配优化三、中间匹配优化四、覆盖索引优化五、减少查询范围六、避免通配符开头七、使用外部搜索引擎八、分

Python实现特殊字符判断并去掉非字母和数字的特殊字符

《Python实现特殊字符判断并去掉非字母和数字的特殊字符》在Python中,可以通过多种方法来判断字符串中是否包含非字母、数字的特殊字符,并将这些特殊字符去掉,本文为大家整理了一些常用的,希望对大家... 目录1. 使用正则表达式判断字符串中是否包含特殊字符去掉字符串中的特殊字符2. 使用 str.isa

Spring Boot 集成 Quartz并使用Cron 表达式实现定时任务

《SpringBoot集成Quartz并使用Cron表达式实现定时任务》本篇文章介绍了如何在SpringBoot中集成Quartz进行定时任务调度,并通过Cron表达式控制任务... 目录前言1. 添加 Quartz 依赖2. 创建 Quartz 任务3. 配置 Quartz 任务调度4. 启动 Sprin