当前位置: 首页 > news >正文

数据结构:哈夫曼树及其哈夫曼编码

目录

        1.哈夫曼树是什么?

        2.哈夫曼编码是什么?

        3.哈夫曼编码的应用

        4.包含头文件

        5.结点设计

        6.接口函数定义

        7.接口函数实现

        8.哈夫曼编码测试案列


哈夫曼树是什么?

        哈夫曼树(Huffman Tree)是一种特殊的二叉树,由David A. Huffman在1952年发明的,用于数据压缩领域。哈夫曼树是一种最优的二叉树,因为它具有最小的加权路径长度。这里的“最优”是指在给定的一组权重(通常是字符出现频率)下,哈夫曼树的加权路径长度(即树中所有叶节点的权重乘以其到根节点的距离)是最小的,以下是哈夫曼树的特点:

        1.完全二叉树:除了最后一层外,每一层都是满的

        2.加权路径长度最小:所有叶节点的权重乘以其到根节点的距离之和是最小的

        3.每个节点都有权重:叶节点代表单个字符,非叶节点代表字符的集合


哈夫曼编码是什么?

        哈夫曼编码是一种使用哈夫曼树进行编码的方法。它将每个字符映射为一个唯一的二进制串,这些二进制串的长度不同,且是根据字符出现频率来确定的。频率越高的字符,其编码越短;频率越低的字符,其编码越长。这种编码方式可以有效地减少数据的存储空间或传输时间。实现哈夫曼编码的步骤如下:

        1.统计字符频率:首先统计数据集中每个字符出现的频率

        2.构建哈夫曼树

                1.将每个字符及其频率作为叶子节点放入优先队列(通常是最小堆)

                2.从队列中取出两个权重最小的节点,创建一个新的内部节点,其权重为这两个节点权重之和

                3.将新节点重新加入队列。重复上述步骤,直到队列中只剩下一个节点,这个节点就是哈夫曼树的根节点

        3.生成编码:从根节点开始,向左子树走标记为0,向右子树走标记为1,直到到达叶节点,此时叶节点对应的字符的路径标记就是其哈夫曼编码


哈夫曼编码的应用

        哈夫曼编码是一种非常实用的编码技术,它通过利用数据的内在特性来优化存储和传输效率:

        1.数据压缩:用于无损数据压缩,特别是在文本压缩中非常有效。

        2.文件压缩:如ZIP文件格式就使用了哈夫曼编码。

        3.通信协议:在某些通信协议中,用于减少传输数据的大小。


包含头文件

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

结点设计

#define Initsize 100
typedef int Elemtype;
int Node[Initsize][2];				//定义二维数组Node存储输入的字符和字符所含的权值
int NodeValue[Initsize];			//定义一维数组NodeValue存储经排序过的字符的权值
int Hand = 0;						//定义整形变量Hand作为数组NodeValue的头指针
int CodeHead = 0;					//定义int类型变量CodeHead作为指针数组Code的头指针typedef struct HTree {Elemtype value;					//存储结点权值Elemtype Lvalue, Rvalue;		//存储孩子标识struct HTree* lchild;			//存储左孩子树struct HTree* rchild;			//存储右孩子树
}HTree,*HfmTree;HfmTree Head;						//定义全局变量Head作为哈夫曼树的根节点指针
HfmTree Code[Initsize];				//定义HTree类型的指针数组Code,存储结点的地址

接口函数定义

void InitHTree(HfmTree& A);		//用于初始化哈夫曼树
void InsertNode(int A);			//用于输入字符和其权值
void SortNodeV(int A);			//用于对输入的权值进行排序
void InitLHfm(HfmTree& A);		//用于哈夫曼树的左子树进行初始化并赋值
void InitRHfm(HfmTree& A);		//用于哈夫曼树的右子树进行初始化并赋值
void InsertHTree(HfmTree& A,int B); //用于创建哈夫曼树
void PostOrder(HfmTree A);	        //用于对哈夫曼树进行后序遍历
void InputBTree(HfmTree A);	        //用于对哈夫曼树的结点权值输出
void SeekHTreeL(HfmTree A, int B);	//用于单独寻找哈夫曼树的左子树的字符及权值
void SeekHTreeR(HfmTree A, int B);	//用于寻找哈夫曼树的右子树的字符及权值
void InputHfmCode(HfmTree A,int B);	//用于输出哈夫曼编码
void InitRootHfm(HfmTree& A,HfmTree &B,HfmTree &C); //用于对哈夫曼树的根结点进行初始化并赋值

接口函数实现

void InputHfmCode(HfmTree A,int B) { //用于输出哈夫曼编码int i,j;while (A != NULL) {				 //对哈夫曼树的左子树进行进栈操作Code[CodeHead] = A;A = A->lchild;CodeHead++;}printf("\n");SeekHTreeL(A, B);		//使用函数SeekHtreeL对其哈夫曼树进行寻找左子树的字符及权值for (i = 1; i < CodeHead; i++) {printf("%d", Code[i]->Lvalue);	//对栈里结点所含的Lvalue进行输出(从根结点开始输出其含有的LvaLue)}CodeHead--;					//出栈操作while (CodeHead != 0) {		//判断栈是否为空printf("\n");SeekHTreeR(A, B);		//使用函数SeekHTreeR对其哈夫曼树进行寻找右子树的字符及权值for (i = 0; i <= CodeHead - 1; i++) {	//对栈里结点所含的Lvalue进行输出(从根结点开始输出其含有的LvaLue)if (i == CodeHead - 1) {				//若为栈尾结点则输出其右子树printf("%d", Code[i]->Rvalue);	break;}printf("%d", Code[i]->Lvalue);}CodeHead--;				//出栈操作}
}void SeekHTreeR(HfmTree A, int B) {	//用于寻找哈夫曼树的右子树的字符及权值int i,j;for (i = CodeHead - 1; i >= 0; i++){ //遍历栈if (i == CodeHead-1) {			 //判断是否为栈的倒数第二个结点(跟定义的结点的有关)for (j = 0; j < B; j++)	{	 //遍历存储字符及权值的数组,寻找对应的字符和权值if (Node[j][1] == Code[i]->rchild->value) {Node[j][1] = -1;	//未防止字符不一样,但权值相同的出现,造成输出哈夫曼编码错误printf("%c的哈夫曼编码为:", Node[j][0]);break;					}}}break;			}
}void SeekHTreeL(HfmTree A, int B){	//用于单独寻找哈夫曼树的左子树的字符及权值int i,j;for (i = 0; i < CodeHead; i++) {	//遍历栈if (i == CodeHead - 1) {		//判断是否为栈的倒数第二个结点(跟定义的结点的有关)for (j = 0; j < B; j++) {	//遍历存储字符及权值的数组,寻找对应的字符和权值if (Node[j][1] == Code[i]->value) {	Node[j][1] = -1;	//未防止字符不一样,但权值相同的出现,造成输出哈夫曼编码错误printf("%c的哈夫曼编码为:", Node[j][0]);break;}}}}
}void InputBTree(HfmTree A) {	//用于对哈夫曼树的结点权值输出		printf("%d   ", A->value);
}void PostOrder(HfmTree A) {		//用于对哈夫曼树进行后序遍历		if (A != NULL) {PostOrder(A->lchild);	PostOrder(A->rchild);	InputBTree(A);			}
}void InsertHTree(HfmTree& A,int B) {//用于创建哈夫曼树if(Hand<B-1){				//判断是否已将所有字符及权值进行构建对应的哈夫曼树的结点if (A == NULL) {		HfmTree Q = (HTree*)malloc(sizeof(HTree));		HfmTree W = (HTree*)malloc(sizeof(HTree));		InitLHfm(Q);				//使用函数InitLHfm对其左子树初始化Hand++;InitRHfm(W);				//使用函数InitRHfm对其右子树初始化InitRootHfm(A, Q, W);		//使用函数InitRootHfm对其根结点初始化Head = A;					//更新哈夫曼树的头指针的指向InsertHTree(A, B);			}else {HfmTree Q = (HTree*)malloc(sizeof(HTree));		HfmTree W = (HTree*)malloc(sizeof(HTree));		Hand++;InitRHfm(Q);				//使用函数InitRHfm对其右子树初始化InitRootHfm(W, A, Q);		//使用函数InitRootHfm对其根结点初始化Head = W;InsertHTree(W, B);}}
}void InitRootHfm(HfmTree& A, HfmTree& B, HfmTree& C) {//用于对哈夫曼树的根结点进行初始化并赋值A = (HTree*)malloc(sizeof(HTree));A->value = B->value + C->value;			//根结点的权值为两个子结点的权值之和A->Lvalue = 1;							//添加左子树标识A->Rvalue = 0;A->lchild = B;							//添加根结点指向的左子树A->rchild = C;							//添加根结点指向的右子树printf("新建的根结点的权值数据为%d\n", A->value);
}void InitRHfm(HfmTree& A) {		//用于哈夫曼树的右子树进行初始化并赋值A->value = NodeValue[Hand];	//对结点所含的权值进行更新A->Lvalue = 0;A->Rvalue = 1;				//添加右子树标识A->lchild = NULL;						A->rchild = NULL;printf("新建的右孩子的权值数据为%d\n", A->value);
}void InitLHfm(HfmTree& A) {			//用于哈夫曼树的左子树进行初始化并赋值A->value = NodeValue[Hand];		//对结点所含的权值进行更新A->Lvalue = 1;					//添加左子树标识A->Rvalue = 0;A->lchild = NULL;A->rchild = NULL;printf("新建的左孩子的权值数据为%d\n", A->value);
}void SortNodeV(int A) {				//用于对输入的权值进行排序int i, j, Q;for (i = 0; i < A - 1; i++) {				//冒泡排序for (j = 0; j < A - 1 - i; j++)if (NodeValue[j] > NodeValue[j + 1]) {Q = NodeValue[j];NodeValue[j] = NodeValue[j + 1];NodeValue[j + 1] = Q;}}
}void InsertNode(int A) {			//用于输入字符和其权值int i, j;char Q;for (i = 0; i < A; i++) {j = 0;printf("请输入结点的字符");getchar();			//清除缓冲区,防止赋值脏数据Q=getchar();Node[i][j] = (int) Q;j++;printf("请输入结点的权值");scanf_s("%d", &Node[i][j]);NodeValue[i] = Node[i][j];}
}void InitHTree(HfmTree& A) {		//用于初始化哈夫曼树A = NULL;printf("初始化哈夫曼树成功\n");
}

哈夫曼编码测试案列

void main() {int NodeSize,i;HfmTree X;InitHTree(X);printf("请问需要输入多少个字符");scanf_s("%d", &NodeSize);InsertNode(NodeSize);SortNodeV(NodeSize);InsertHTree(X, NodeSize);printf("创建哈夫曼树成功\n");printf("后序遍历的哈夫曼树为:");PostOrder(Head);printf("\n");InputHfmCode(Head,NodeSize);
}

相关文章:

数据结构:哈夫曼树及其哈夫曼编码

目录 1.哈夫曼树是什么&#xff1f; 2.哈夫曼编码是什么&#xff1f; 3.哈夫曼编码的应用 4.包含头文件 5.结点设计 6.接口函数定义 7.接口函数实现 8.哈夫曼编码测试案列 哈夫曼树是什么&#xff1f; 哈夫曼树&#xff08;Huffman Tree&#xff09;是一种特殊的二叉树&#xf…...

微信如何防止被对方拉黑删除?一招教你解决!文末附软件!

你一定不知道&#xff0c;微信可以防止被对方拉黑删除&#xff0c;秒变无敌。只需一招就能解决&#xff01;赶快来学&#xff01;文末有惊喜&#xff01; 惹到某些重要人物&#xff08;比如女朋友&#xff09;&#xff0c;被删除拉黑一条龙&#xff0c;那真的是太令人沮丧了&a…...

jar增量打包

jar增量打包 Linux环境下&#xff1a; 1.解压缩 jar -xvf jarname.jar&#xff08;解压&#xff09;2.打包 这时可以把要替换的lib包的内容粘帖进去&#xff0c;然后重新打jar包 jar -cvf0M jarname.jar .&#xff08;重新压缩,-0是主要的&#xff09;jar命令&#xff1a; …...

智慧医院物联网建设-统一管理物联网终端及应用

近年来&#xff0c;国家卫健委相继出台的政策和评估标准体系中&#xff0c;都涵盖了强化物联网建设的内容。物联网建设已成为智慧医院建设的核心议题之一。 作为医院高质量发展的关键驱动力&#xff0c;物联网的顶层设计与网络架构设计规划&#xff0c;既需要结合现代信息技术的…...

Debian的常用命令

Debian作为一个稳定、安全且高效的Linux发行版,被广泛应用于服务器和桌面操作系统中。对于系统管理员和开发者来说,熟练掌握Debian的常用命令能够大大提升工作的效率和系统的管理水平。本文将详细介绍一些常见且实用的Debian命令,帮助新手更好地管理和操作Debian系统。 系统…...

矩阵1-范数与二重求和的求和可交换

矩阵1-范数与二重求和的求和可交换 1、矩阵1-范数 A [ a 11 a 12 ⋯ a 1 n a 21 a 22 ⋯ a 2 n ⋮ ⋮ ⋱ ⋮ a n 1 a n 2 ⋯ a n n ] A \begin{bmatrix} a_{11} &a_{12} &\cdots &a_{1n} \\ a_{21} &a_{22} &\cdots &a_{2n} \\ \vdots &\vdots …...

Python笔记 - *args和**kwargs

探索Python的*args和**kwargs 在Python中&#xff0c;函数可以接受任意数量的参数&#xff0c;而这要归功于*args和**kwargs的强大功能。这两个特性使得函数在处理不同数量的输入时变得更加灵活和高效。在这篇博客中&#xff0c;我们将详细介绍*args和**kwargs&#xff0c;并展…...

微信小程序实现图片转base64

在微信小程序中&#xff0c;图片转base63可以引入第三方插件&#xff1b; 也可以通过下边的方法转base64。 转换方法&#xff1a; imgToBase64(filePath) {return new Promise((resolve, reject) > {let baseFormat data:image/png;base64,let base64 wx.getFileSystem…...

os和os.path模块

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 目录也称文件夹&#xff0c;用于分层保存文件。通过目录可以分门别类地存放文件。我们也可以通过目录快速找到想要的文件。在Python中&#xff0c;并…...

链表题目练习----重排链表

这道题会联系到前面写的一篇文章----快慢指针相关经典问题。 重排链表 指针法 这道题乍一看&#xff0c;好像有点难处理&#xff0c;但如果仔细观察就会发现&#xff0c;这道题是查找中间节点反转链表链表的合并问题&#xff0c;具体细节有些不同&#xff0c;这个在反装中间链…...

【杂记-浅谈XSS跨站脚本攻击】

一、什么是XSS&#xff1f; XSS&#xff0c;Cross-site Scripting&#xff0c;跨站脚本攻击&#xff0c;是一种典型的Web程序漏洞利用攻击&#xff0c;攻击者利用Web程序对用户输入检查不足的漏洞将可执行恶意脚本注入网站或Web应用&#xff0c;当用户访问网页时触发恶意脚本的…...

VMware虚拟机与MobaXterm建立远程连接失败

VMware虚拟机与MobaXterm建立远程连接失败 首先可以检查一下是不是虚拟机的ssh服务并不存在 解决方法&#xff1a; 1.更新镜像源 yum -y update 这个过程会有点久&#xff0c;请耐心等待 2.安装ssh yum install openssh-server 3.启动ssh systemctl restart sshd 4.查…...

mysql undolog管理

在MySQL中&#xff0c;Undo Log&#xff08;撤销日志&#xff09;用于支持事务的回滚和MVCC&#xff08;多版本并发控制&#xff09;。为了避免Undo Log不断增长&#xff0c;影响系统性能&#xff0c;需要进行合理的清理。MySQL的Undo Log清理策略主要依赖于系统的配置参数和后…...

【Linux】进程2——管理概念,进程概念

1.什么是管理&#xff1f; 那在还没有学习进程之前&#xff0c;就问大家&#xff0c;操作系统是怎么管理进行进程管理的呢&#xff1f; 很简单&#xff0c;先把进程描述起来&#xff0c;再把进程组织起来&#xff01; 我们拿大学为例子 最典型的管理者——校长最典型的被管理…...

【C++】植物大战僵尸杂交版自动存档——防闪退存档消失

植物大战僵尸杂交版现已更新到v2.0.88&#xff0c;闪退问题还是偶有发生&#xff0c;参考网上现有的方案&#xff0c;简单实现了一个。 原理就是监控存档目录的文件变化&#xff0c;一旦有新的存档&#xff0c;则将其备份。如发生闪退&#xff0c;则还原备份即可。 原目录&…...

通过Excel,生成sql,将A表数据插入B表

文章目录 投机取巧的方式,进行表数据初始化通过navicat搜索A表数据,然后复制进excel中通过excel的函数方式,将该批量数据自动生成插入B表的sql语句然后一次性拷贝生成的sql语句,放进navicat中一次执行,直接完成数据初始化...

如何在MySQL中实现upsert:如果不存在则插入?

目录 1 使用 REPLACE 2 使用 INSERT ... ON DUPLICATE KEY UPDATE 使用 INSERT IGNORE 有效会导致 MySQL 在尝试执行语句时忽略执行错误 INSERT 。这意味着 包含 索引或 字段 INSERT IGNORE 中重复值的语句 不会 产生错误&#xff0c;而只是完全忽略该特定 命令。其明显目的是…...

MyBatis中 set标签

1、set标签特点&#xff1a; set标签用于更新语句中set标签解析为set关键字set可以去除跟新语句中无用的逗号通常是和if标签一起使用 2、set标签的使用 编写接口方法编写sql语句 注意 当set标签中有条件成立时就会附加set关键字&#xff0c;字段为null时该列不会被更新。se…...

mysql自带分页

select 查询列表 from 表 limit offset,pagesize; offset代表的是起始的条目索引&#xff0c;默认从0开始size代表的是显示的条目数offset(n-1)*pagesize -- 第-页 limit 0 5 -- 第二页 limit 5,5 -- 第三页 limit 10,5 -- 第n页limit(n-1)*pagesize,pagesize -- pages…...

小学一年级数学上册,我终于学完了

目录 一、背景二、过程1.我对课程中的一些知识的思考2.我对于产品的思考3.我对自己儿子与知识产品结合的思考4.产品反馈的那些有意思的数据 三、总结 一、背景 简约而不简单&#xff0c;即是曾经的再现&#xff0c;也是未来的延伸&#xff0c;未来已来&#xff0c;就在脚下。 …...

使用wireshark分析tcp握手过程

开启抓包 tcpdump -i any host 127.0.0.1 and port 123 -w tcp_capture.pcap 使用telnet模拟tcp连接 telnet 127.0.0.1 123 如果地址无法连接&#xff0c;则会一直重试SYN包&#xff0c;各个平台SYN重试间隔并不一致&#xff0c;如下&#xff1a; 异常站点抓包展示&#xff…...

在ArcGIS中,矢量数据有.shp,.mdb和.gdb,为啥建议使用gdb?

在ArcGIS中,矢量数据可以存储在多种格式中,如 .shp (Shapefile)、.mdb (Microsoft Access Database) 和 .gdb (Geodatabase)。每种格式都有其特定的用途和优缺点,但通常推荐使用 Geodatabase(.gdb)格式,原因如下: 1. 更高的数据容量和性能 容量: Shapefiles 和 MDB 文…...

C++STL---stack queue模拟实现

前言 对于这两个容器适配器的模拟实现非常简单&#xff0c;因为stack和queue只是对其他容器的接口进行了包装&#xff0c;在STL中&#xff0c;若我们不指明用哪种容器作为底层实现&#xff0c;栈和队列都默认是又deque作为底层实现的。 也就是说&#xff0c;stack和queue不管是…...

Spring Cloud系列——使用Sentinel进行微服务保护

文章目录 一、引言1. 雪崩问题的产生原因2. 解决雪崩问题的思路 二、微服务保护1. 服务保护方案1.1 请求限流1.2 线程隔离1.3 服务熔断 2. Sentinel2.1 安装2.2 微服务整合2.2.1 请求限流2.2.2 线程隔离①OpenFeign整合Sentinel②配置线程隔离 2.2.3 服务熔断①编写降级逻辑②配…...

Android开机动画,framework修改Bootanimation绘制文字。

文章目录 Android开机动画&#xff0c;framework修改Bootanimation动画绘制文字。opengl绘制源码分析 Android开机动画&#xff0c;framework修改Bootanimation动画绘制文字。 frameworks/base/cmds/bootanimation/bootanimation.cpp 绘制时间的一个方法 // We render 12 or …...

2024河南高考作文ChatGPT

阅读下面的材料&#xff0c;根据要求写作。&#xff08;60分&#xff09; 随着互联网的普及、人工智能的应用&#xff0c;越来越多的问题能很快得到答案。那么&#xff0c;我们的问题是否会越来越少&#xff1f; 以上材料引发了你怎样的联想和思考&#xff1f;请写一篇文章。 要…...

整理好了!2024年最常见 20 道分布式、微服务面试题(一)

一、什么是分布式系统&#xff1f; 分布式系统是由多个独立的计算机&#xff08;通常称为节点&#xff09;组成的系统&#xff0c;这些计算机通过网络连接在一起&#xff0c;协同工作以完成一个共同的任务或服务。以下是分布式系统的关键特点和概念&#xff1a; 网络依赖性&am…...

要想数据形成好的数据集,必须数据治理(目的之一是防止大模型产生灰色数据等),用于炼丹(训练数据私有化模型)的数据才是好数据

数据治理&#xff1a;必要性、实施方法及挑战 引言 在当今数字化时代&#xff0c;数据已经成为企业最重要的资产之一。随着数据量的爆炸性增长&#xff0c;如何有效地管理和利用数据成为企业面临的重大挑战。数据治理&#xff08;Data Governance&#xff09;作为一种系统化的…...

外部mysql导入

利用这个命令&#xff1a; mysql -u username -p database_name < file.sql 然后就这样。成功导入。...

Qwen-VL论文阅读

论文地址 其他同学的详细讲解 模型结构和参数大小 &#xff08;1&#xff09;LLM&#xff1a;Qwen-7B &#xff08;2&#xff09;Vision Encoder&#xff1a;ViT架构&#xff0c;初始化参数是 Openclip’s ViT-bigG。 在训练和推理过程中&#xff0c;输入的图像都被调整到…...