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

相关文章

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount