数据结构与算法基础-学习-18-哈夫曼编码
一、个人理解
在远程通讯中,需要把字符转成二进制的字符串进行传输,例如我们需要传输ABCD,我们可以用定长的字符串进行表示,例如:
A:00
B:01
C:02
D:03
这样可能就造成空间的浪费,我们多存储了一个0号位。那用变长呢,会怎么样,我们试试,例如:
A:0
B:00
C:01
D:1
如果接收到的二进制字符串为:0000101,在解码时是否就会出现歧义,可以是AAAADAD,也可以是ABCC,也可以是BBDC等等。
从而引入了哈夫曼编码,也称为最优前缀码。之前我们已经实现了哈夫曼树,可以参考文章链接《数据结构与算法基础-学习-17-二叉树之哈夫曼树》,从哈夫曼树这个中间结果,转换出我们想要的哈夫曼编码。
二、为什么哈夫曼编码得出二进制字符串最短呢?
在生成哈夫曼树之前,我们需要统计字符在数据中出现的概率,出现的概率越高,编码会越短。
之前介绍过的哈夫曼树的特点,权值越大的结点离根结点越近,也就是路径越短。
哈夫曼树规定走左子树就标记为0,走右子树就标记为1,从根结点到各个叶子节点的标记拼接起来,就是这个字符的哈夫曼编码。
三、结构体定义
typedef char** HaffmanCode;
四、函数实现
1、生成哈夫曼编码
(1)定义
Status CreateHaffmanCode(HaffmanTree HT, HaffmanCode* HC, HaffmanTreeLentype ArrayLen)
{JudgeAllNullPointer(HT);//Init HaffmanCode*HC = (HaffmanCode)MyMalloc(sizeof(char*) * ArrayLen);char* TmpHC = (char*)MyMalloc(sizeof(char) * (ArrayLen - 1));//临时数组存放哈夫曼编码中间结果,长度n,编码最长需要n-1,所以分配n-1。HaffmanTreeLentype TmpHCLen = 0;HaffmanTreeLentype i,j;HaffmanTreeLentype TmpParentIndex;//上一个结点的父亲索引。HaffmanTreeLentype TmpIndex;//上一个结点的索引号。for(i = 1; i <= ArrayLen; i++){TmpParentIndex = HT[i].ParentIndex;TmpIndex = i;while(HT[TmpParentIndex].ParentIndex != 0){if(HT[TmpParentIndex].LeftChildIndex == TmpIndex){TmpHC[TmpHCLen] = '0';TmpHCLen++;}else if (HT[TmpParentIndex].RightChildIndex == TmpIndex){TmpHC[TmpHCLen] = '1';TmpHCLen++;}else{Log("Internal Error : Perant LeftChildIndex and RightChildIndex != TmpIndex",Error);}TmpIndex = TmpParentIndex;TmpParentIndex = HT[TmpParentIndex].ParentIndex;}//根节点判断if(HT[TmpParentIndex].LeftChildIndex == TmpIndex){TmpHC[TmpHCLen] = '0';TmpHCLen++;}else if(HT[TmpParentIndex].RightChildIndex == TmpIndex){TmpHC[TmpHCLen] = '1';TmpHCLen++;}else{Log("Internal Error : Root Node Perant LeftChildIndex and RightChildIndex != TmpIndex",Error);}//将TmpHC中的字符倒序写入HC中。(*HC)[i-1] = (char*)MyMalloc(sizeof(char) * (TmpHCLen + 1));//编码最长需要n,加上\0,所以分配n+1。for(j = TmpHCLen - 1; j >= 0; j--){(*HC)[i-1][TmpHCLen - 1 - j] = TmpHC[j];if(j == 0)//因为j是无符号长整型,j不能等于负数,当j=-1,退出循环,导致程序core掉,由此添加此判断,避免问题的发生。{break;}}(*HC)[i-1][TmpHCLen] = '\0';TmpHCLen = 0;}free(TmpHC);TmpHC = NULL;Log("Create HaffmanCode : OK\n",Info);return SuccessFlag;
}
(2)参数
参数名 | 说明 |
HT | 传入参数类型为HaffmanTree 的哈夫曼树。 |
HC | 传入参数类型为HaffmanCode*的哈夫曼编码数组指针。 |
ArrayLen | 传入参数类型为HaffmanTreeLentype的权值数组长度。 |
(3)实现思路
A,B,C,D,E对应的权值如下:
HaffmanTreeLentype WeightArray[] = {2,4,2,3,3};
生成哈夫曼树如下:
2023-3--[Info]--HaffmanTree :
[ Index | Weight | ParentIndex | LeftChildIndex | RightChildIndex ]
[ 0 | 0 | 0 | 0 | 0 ]
[ 1 | 2 | 6 | 0 | 0 ]
[ 2 | 4 | 8 | 0 | 0 ]
[ 3 | 2 | 6 | 0 | 0 ]
[ 4 | 3 | 7 | 0 | 0 ]
[ 5 | 3 | 7 | 0 | 0 ]
[ 6 | 4 | 8 | 1 | 3 ]
[ 7 | 6 | 9 | 4 | 5 ]
[ 8 | 8 | 9 | 6 | 2 ]
[ 9 | 14 | 0 | 7 | 8 ]
图示如下:

从根节点开始遍历到每个叶子结点算出哈夫曼编码比较困难,我们反其道而行之,从叶子结点开始遍历到根节点,但我们算出的结果是一个倒序的,我们需要一个临时数组来存储倒序编码,最后再正序填写回哈夫曼编码数组中即可。
例如我们来算一个A,父亲是权值4,走左子树为0,0写入临时数组,
char TmpHC[] = {'0'};
再往上走,父亲是权值8,走左子树为0,0写入临时数组,
char TmpHC[] = {'0','0'};
再往上走,父亲是权值14,走右子树为1,1写入临时数组,
char TmpHC[] = {'0','0','1'};
倒序写回哈夫曼编码数组就是100。其他的大家自己可以算一下,是不是这样,至于和老师讲的可能有不一样的地方,例如最后形成的编码,只是由于数组中选出两个最小的值,哪个是左子树索引,哪个是右子树索引,不一样导致,但不影响编码。最终得出的哈夫曼编码如下:
2023-3--[Info]--HaffmanCode :
[ Index | Code ]
[ 1 | 100 ]
[ 2 | 11 ]
[ 3 | 101 ]
[ 4 | 00 ]
[ 5 | 01 ]
2、销毁哈夫曼编码
(1)定义
Status DestoryHaffmanCode(HaffmanCode HC,HaffmanTreeLentype ArrayLen)
{JudgeAllNullPointer(HC);HaffmanTreeLentype i;for(i = 0; i < ArrayLen; i++){free(HC[i]);HC[i] = NULL;}free(HC);HC = NULL;Log("Destory HaffmanCode : OK\n",Info);return SuccessFlag;
}
(2)参数
参数名 | 说明 |
HC | 传入参数类型为HaffmanCode*的哈夫曼编码数组指针。 |
ArrayLen | 传入参数类型为HaffmanTreeLentype的权值数组长度。 |
四、虚机测试
[gbase@czg2 Tree]$ make
gcc -Wall -g ../Log/Log.c BinaryTree.c HaffmanTree.c main.c -o TestBinaryTree -I ../Log/
[gbase@czg2 Tree]$ ./TestBinaryTree
2023-3--[Info]--Init Global Array : OK
2023-3--[Info]--Init Binary Tree : OK
2023-3--[Info]--New Binary Search Tree : OK
2023-3--[Info]--PreOrderTraverse : [6 ,3 ,2 ,1 ,5 ,8 ,7 ], CurSize : 7
2023-3--[Info]--InOrderTraverse : [1 ,2 ,3 ,5 ,6 ,7 ,8 ], CurSize : 7
2023-3--[Info]--PostOrderTraverse : [1 ,2 ,5 ,3 ,7 ,8 ,6 ], CurSize : 7
2023-3--[Info]--PreOrderTraverseNoRcs : [6 ,3 ,2 ,1 ,5 ,8 ,7 ], CurSize : 7
2023-3--[Info]--InOrderTraverseNoRcs : [1 ,2 ,3 ,5 ,6 ,7 ,8 ], CurSize : 7
2023-3--[Info]--PostOrderTraverseNoRcs : [1 ,2 ,5 ,3 ,7 ,8 ,6 ], CurSize : 7
2023-3--[Info]--LevelOrderBinaryTree : [6 ,3 ,8 ,2 ,5 ,7 ,1 ], CurSize : 7
2023-3--[Info]--SqQueue Data :
Data : [1 ,2 ,3 ,0 ,0 ,4 ,5 ,0 ,6 ,0 ,0 ,7 ,0 ,0 ,0 ]
FrontIndex : 0
RearIndex : 15
SqQueueLen : 15
2023-3--[Info]--Init Binary Tree : OK
2023-3--[Info]--PreOrderTraverse : [1 ,2 ,3 ,4 ,5 ,6 ,7 ], CurSize : 7
2023-3--[Info]--InOrderTraverse : [3 ,2 ,5 ,6 ,4 ,7 ,1 ], CurSize : 7
2023-3--[Info]--PostOrderTraverse : [3 ,6 ,5 ,7 ,4 ,2 ,1 ], CurSize : 7
2023-3--[Info]--PreOrderTraverseNoRcs : [1 ,2 ,3 ,4 ,5 ,6 ,7 ], CurSize : 7
2023-3--[Info]--InOrderTraverseNoRcs : [3 ,2 ,5 ,6 ,4 ,7 ,1 ], CurSize : 7
2023-3--[Info]--PostOrderTraverseNoRcs : [3 ,6 ,5 ,7 ,4 ,2 ,1 ], CurSize : 7
2023-3--[Info]--LevelOrderBinaryTree : [1 ,2 ,3 ,4 ,5 ,7 ,6 ], CurSize : 7
2023-3--[Info]--Init Binary Tree : OK
2023-3--[Info]--Copy Tree : OK
2023-3--[Info]--PreOrderTraverse : [1 ,2 ,3 ,4 ,5 ,6 ,7 ], CurSize : 7
2023-3--[Info]--InOrderTraverse : [3 ,2 ,5 ,6 ,4 ,7 ,1 ], CurSize : 7
2023-3--[Info]--PostOrderTraverse : [3 ,6 ,5 ,7 ,4 ,2 ,1 ], CurSize : 7
2023-3--[Info]--Tree Depth : 4
2023-3--[Info]--Tree Depth : 5
2023-3--[Info]--Tree Depth : 5
2023-3--[Info]--Tree Node Num : 7
2023-3--[Info]--Tree Node Num : 7
2023-3--[Info]--Tree Node Num : 7
2023-3--[Info]--Tree Leaves Node Num : 3
2023-3--[Info]--Tree Leaves Node Num : 3
2023-3--[Info]--Tree Leaves Node Num : 3
2023-3--[Info]--Create HaffmanTree : OK
2023-3--[Info]--HaffmanTree :
[ Index | Weight | ParentIndex | LeftChildIndex | RightChildIndex ]
[ 0 | 0 | 0 | 0 | 0 ]
[ 1 | 2 | 6 | 0 | 0 ]
[ 2 | 4 | 8 | 0 | 0 ]
[ 3 | 2 | 6 | 0 | 0 ]
[ 4 | 3 | 7 | 0 | 0 ]
[ 5 | 3 | 7 | 0 | 0 ]
[ 6 | 4 | 8 | 1 | 3 ]
[ 7 | 6 | 9 | 4 | 5 ]
[ 8 | 8 | 9 | 6 | 2 ]
[ 9 | 14 | 0 | 7 | 8 ]
2023-3--[Info]--Create HaffmanCode : OK
2023-3--[Info]--HaffmanCode :
[ Index | Code ]
[ 1 | 100 ]
[ 2 | 11 ]
[ 3 | 101 ]
[ 4 | 00 ]
[ 5 | 01 ]
2023-3--[Info]--Destory HaffmanCode : OK
2023-3--[Info]--Destory HaffmanTree : OK
2023-3--[Info]--Destroy BT Node : OK
2023-3--[Info]--Destroy BT Node : OK
2023-3--[Info]--Destroy BT Node : OK
2023-3--[Info]--Destroy BT Node : OK
2023-3--[Info]--Destroy BT Node : OK
2023-3--[Info]--Destroy BT Node : OK
2023-3--[Info]--Destroy BT Node : OK
2023-3--[Info]--Destroy BT Node : OK
2023-3--[Info]--Destroy BT Node : OK
2023-3--[Info]--Destroy BT Node : OK
2023-3--[Info]--Destroy BT Node : OK
2023-3--[Info]--Destroy BT Node : OK
2023-3--[Info]--Destroy BT Node : OK
2023-3--[Info]--Destroy BT Node : OK
2023-3--[Info]--Destroy BT Node : OK
2023-3--[Info]--Destroy BT Node : OK
2023-3--[Info]--Destroy BT Node : OK
2023-3--[Info]--Destroy BT Node : OK
2023-3--[Info]--Destroy BT Node : OK
2023-3--[Info]--Destroy BT Node : OK
2023-3--[Info]--Destroy BT Node : OK
相关文章:

数据结构与算法基础-学习-18-哈夫曼编码
一、个人理解在远程通讯中,需要把字符转成二进制的字符串进行传输,例如我们需要传输ABCD,我们可以用定长的字符串进行表示,例如:A:00B:01C:02D:03这样可能就造成空间的浪费,我们多存储了一个0号位。那用变长呢…...

ZMC408CE | 实现“8通道独立PSO”应用场景
一、ZMC408SCAN产品亮点 1.高性能处理器,提升运算速度、响应时间和扫描周期等; 2.一维/二维/三维、多通道视觉飞拍,高速高精; 3.位置同步输出PSO,连续轨迹加工中对精密点胶胶量控制和激光能量控制等; 4…...
QuickJS中JS_SetClassProto方法把JavaScript对象指定为某个类的原型对象
在 QuickJS 中,JS_SetClassProto 方法用于设置一个类的原型对象。这个方法的作用是将一个 JavaScript 对象指定为该类的原型对象,从而定义该类的属性和方法。 具体来说,JS_SetClassProto 方法的第一个参数是指向 QuickJS 引擎执行上下文的指…...

泰克信号发生器特点
泰克信号发生器是一种用于产生各种类型的电子信号的仪器,可以广泛应用于电子、通信、自动化、医疗等领域。泰克信号发生器具有以下特点:多种信号类型:泰克信号发生器可以产生多种类型的电子信号,包括正弦波、方波、三角波、脉冲等…...

贯穿设计模式第四话--里氏替换原则
🥳🥳🥳 茫茫人海千千万万,感谢这一刻你看到了我的文章,感谢观赏,大家好呀,我是最爱吃鱼罐头,大家可以叫鱼罐头呦~🥳🥳🥳 从今天开始,将…...
6501: 鸡兔同笼
描述 一个笼子里面关了鸡和免子(鸡有两只脚,兔子有4只脚,没有例外)。已经知道了笼子里面脚的总数a,问笼子里面至少有多少只动物,至多有多少只动物。 输入 一个正整数a(a<32768)。 输出 包含两个正整数,第一个是最少的动物数,第二个是最多的…...

Linux项目自动化构建工具-make/makefile 介绍及使用
使用背景 在工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义一系列 规则来指定什么文件需要先编译,什么文件需要后编译,哪些文件需要重新编译,或者更复杂 的功能操作 makefile带来的好处…...

【云原生|Docker】06-dokcerfile详解
目录 前言 Dockerfile基础示例 Dockerfile简介 1. Dockerfile概念 2. Dokcer镜像分层理解 3. Doker build构建原理 Dockerfile参数解析 1. Dokcerfile组成 2. 指令说明 2.1 FROM引入基础镜像 2.2 LABEL 2.3 ENV 2.4 RUN 2.5 COPY 2.6 ADD 2…...

【SCL】博图——先入先出排序法
使用博图SCL语言来实现先入先出排序 前言 使用SCL完成一个先入先出排序 具体要求:最先输入的一个数值,最先输出出来,下面的数自动向前填充; 注:这里可能有两种理解:一是第一个输入的第一个出来ÿ…...

OSPF----特殊区域
目录 OSPF----特殊区域 第一大类----末梢区域(Stub Area) 完全末梢区域((Totally Stub Area) 第二大类特殊区域----非完全末梢区域(NSSA) OSPF----特殊区域 第一大类----末梢区域(Stub Area)…...

JVM-类加载
1:类加载机制: 加、验、准、解、初、使、卸 加、烟、准、姐、初、湿、鞋 加载、将class 文件转化为二进制流加载 JVM 内存中并生成一个该类的Class对象验证、Class 文件的字节流中包含的信息是否符合当前虚拟机的要求准备、在方法区中分配这些变量所…...

超详细讲解C语言文件操作!!
超详细讲解C语言文件操作!!什么是文件文件名文件的打开和关闭文件指针文件的打开和关闭文件的顺序读写文件的随机读写fseekftellrewind文本文件和二进制文件文件读取结束的判定文件缓冲区什么是文件 磁盘上的文件是文件。但是在程序设计中,我…...

linxu学习之进程
文章目录进程程序和进程产生进程销毁进程多进程高并发设计孤儿僵尸守护进程孤儿进程:守护进程(重点)僵尸进程:进程 程序和进程 操作系统可以运行多个程序,那他是如何运行的?实际上,CPU的执行是很快的,而待…...
蓝桥杯真题2
[蓝桥杯 2013 省 B] 连号区间数 题目描述 小明这些天一直在思考这样一个奇怪而有趣的问题: 在 111 ~ NNN 的某个全排列中有多少个连号区间呢?这里所说的连号区间的定义是: 如果区间 [L,R][L, R][L,R] 里的所有元素(即此排列的…...

PWM互补输出,以及死区时间计算
本文基于野火例程进行解说 实验内容 本次实验输出一对互补的pwm波,且进行死区时间的计算说明。 代码 互补输出对应的定时器初始化代码: bsp_advance_tim.c /********************************************************************************* fi…...

基于深度学习的海洋动物检测系统(Python+YOLOv5+清新界面)
摘要:基于深度学习的海洋动物检测系统使用深度学习技术检测常见海洋动物,识别图片、视频和实时视频中的海洋动物,方便记录、展示和保存结果。本文详细介绍海洋动物检测系统,在介绍算法原理的同时,给出Python的实现代码…...
C# 计算方差
50,100,100,60,50 计算他们的方差 为了计算这些数的方差,需要进行以下步骤: 1. 计算平均值,即将这些数相加,然后除以它们的数量。 平均值 (50 100 100 60 50) / 5 72 2. 计…...

HJZS电源监视继电器HJZS-E202 AC220V
系列型号: HJZS-E202断电延时继电器 HJZS-E002断电延时继电器 一 应用 HJZS-E202电源监视继电器用于直流或交流操作的各种保护和自动控制的装置中,用以增加触点数量。 二 安装结构 导轨安装9壳体结构,具体尺寸参阅外型尺寸图。 三 产品型号…...

dolphinscheduler 2.0.6 资源中心改造方案二:通过NFS挂载共享目录
目录调度资源中心存储概要安装NFS服务器客户端调度验证关闭SFTP开关(可忽略)重新上传资源文件worker执行任务验证服务器woker客户端worker其它nfs共享目录的配置文件/etc/exports说明调度资源中心存储概要 针对现有的单机存储可以做哪些扩展?…...

基于集成学习的用户流失预测并利用shap进行特征解释
基于集成学习的用户流失预测并利用shap进行特征解释 小P:小H,如果我只想尽可能的提高准确率,有什么好的办法吗? 小H:优化数据、调参侠、集成学习都可以啊 小P:什么是集成学习啊,听起来就很厉害的…...

手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...

基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...

C++ 设计模式 《小明的奶茶加料风波》
👨🎓 模式名称:装饰器模式(Decorator Pattern) 👦 小明最近上线了校园奶茶配送功能,业务火爆,大家都在加料: 有的同学要加波霸 🟤,有的要加椰果…...
【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)
LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...

数据结构第5章:树和二叉树完全指南(自整理详细图文笔记)
名人说:莫道桑榆晚,为霞尚满天。——刘禹锡(刘梦得,诗豪) 原创笔记:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 上一篇:《数据结构第4章 数组和广义表》…...