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

相关文章

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

MySQL8.0设置redo缓存大小的实现

《MySQL8.0设置redo缓存大小的实现》本文主要在MySQL8.0.30及之后版本中使用innodb_redo_log_capacity参数在线更改redo缓存文件大小,下面就来介绍一下,具有一... mysql 8.0.30及之后版本可以使用innodb_redo_log_capacity参数来更改

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没