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

目录

        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);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/24293.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Message forwarding mechanism (消息转发机制)

iOS的消息转发机制 iOS的消息转发机制是在消息发送给对象时&#xff0c;找不到对应的实例方法的情况下启动的。消息转发允许对象在运行时处理无法识别的消息&#xff0c;提供了一种动态的、灵活的消息处理方式。 消息转发机制主要分为三个阶段&#xff1a; 动态方法解析快速…

香港高才通计划续签,很可能大部分人都要烂尾

自2022年12月28日推出以来&#xff0c;截至2024年2月29日&#xff0c;高才通计划申请人数超过了7万宗&#xff0c;获批人数将近6万。 这张表格是从2022年到2023年每个月申请人数情况&#xff0c;高峰时间段达到了一个月7166人申请&#xff1a; 香港高才通计划申请人数72508人&…

PostgreSQL基础(九):PostgreSQL的事务介绍

文章目录 PostgreSQL的事务介绍 一、什么是ACID&#xff08;常识&#xff09; 二、事务的基本使用 三、保存点&#xff08;了解&#xff09; PostgreSQL的事务介绍 一、什么是ACID&#xff08;常识&#xff09; 在日常操作中&#xff0c;对于一组相关操作&#xff0c;通常…

python数据分析-问卷数据分析(地理课)

学生问卷 分析学生背景&#xff1a;班级分布、每周地理课数量、地理成绩分布 根据问卷&#xff0c;可以知道&#xff1a; 班级分布&#xff1a; 七年级有118名学生。 八年级有107名学生。 每周地理课的数量&#xff1a; 有28名学生每周有1节地理课。 有99名学生每周有2…

重新学习STM32(2)NVIC

概念简介 NVIC&#xff0c;即嵌套向量中断控制器&#xff0c;控制着中断相关的功能&#xff0c;是内核里面的一个外设。 中断在单片机编程中的作用是使单片机能及时响应需要立即处理的事件&#xff0c;但是这些事件也分紧急和非紧急&#xff0c;因此需要优先级来区分。…

Mysql基础教程(14):UNION

MySQL UNION 的用法与实例 在本文中&#xff0c;我们讨论在 MySQL 中对两个结果集进行 UNION 运算&#xff0c;以及 UNION 运算的规则。 在 MySQL 中&#xff0c;UNION 操作符是一个集合操作符&#xff0c;它用于合并 【2 个结果集】中的所有的行。 SQL 标准中定义了 3 个集…

MongoDB 正则表达式详解:高效数据查询与处理技巧

MongoDB 的正则表达式&#xff08;Regular Expression&#xff09;功能允许在查询中进行模式匹配和文本搜索&#xff0c;为用户提供了强大的灵活性。 基本语法 MongoDB 中使用正则表达式时&#xff0c;通常是在查询语句中使用 $regex 操作符。基本语法如下&#xff1a; { &l…

Spring AI 第二讲 之 Chat Model API 第八节Anthropic 3 Chat

Anthropic Claude 是一系列基础人工智能模型&#xff0c;可用于各种应用。对于开发人员和企业来说&#xff0c;您可以利用 API 访问&#xff0c;直接在 Anthropic 的人工智能基础架构之上进行构建。 Spring AI 支持用于同步和流式文本生成的 Anthropic 消息 API。 Anthropic …

MSA(the Method of Successive Averages)算法的满足条件是怎么来的?

文章目录 摘要前言MSA算法MSA算法简介与来源算法收敛性证明使用MSA算法对确定性网络均衡问题收敛性的验证使用MSA算法对随机网络均衡问题收敛性的验证 此文章属于文献研读内容&#xff0c;文章内容来源于以下文献 Warren B. Powell, Yosef Sheffi , (1982) The Convergence of …

二轴机器人大米装箱机:技术创新引领智能包装新潮流

在科技日新月异的今天&#xff0c;自动化和智能化已成为各行各业追求高效、精准生产的关键。作为粮食加工行业的重要一环&#xff0c;大米装箱机的技术创新与应用价值日益凸显。其中&#xff0c;二轴机器人大米装箱机以其高效、稳定、智能的特点&#xff0c;成为市场的新宠。星…

Python怎么循环计数:深入解析与实践

Python怎么循环计数&#xff1a;深入解析与实践 在Python编程中&#xff0c;循环计数是一项基础且重要的技能。无论是处理列表、遍历文件&#xff0c;还是执行重复任务&#xff0c;循环计数都发挥着不可或缺的作用。本文将从四个方面、五个方面、六个方面和七个方面详细阐述Py…

Python爬虫小练习

爬虫的本质 爬虫的本质就是通过程序模拟正常人向网站发送请求获取信息。 关于爬虫的一些闲聊 按照我们的常识来说&#xff0c;我们不可能在1秒钟访问这个网站100次&#xff0c;请求100次数据&#xff0c;所以过多的请求很有可能会被网站认为你在使用脚本进行爬虫&#xff0c…

从入门到精通:基础IO

引言 在编程的世界里&#xff0c;文件输入输出&#xff08;IO&#xff09;是与操作系统交互的重要方式。无论你是开发应用程序、处理数据&#xff0c;还是管理系统资源&#xff0c;掌握文件IO操作都是必不可少的。本篇博客将带你深入了解C语言中的基础IO操作&#xff0c;从入门…

使用 IPSET 添加 CDN 节点 IP(IPv4/IPv6)到防火墙白名单

明月的服务器一直使用的是 iptables,随着近几年 IPv6 的普及&#xff0c;明月切身体会到还是 IPSET 最方便了&#xff0c;无论你是 IPv4 还是 IPv6 都可以方便的管理&#xff0c;无论你是加入白名单还是黑名单&#xff0c;都非常的简单高效&#xff01;今天就参照明月自己的实操…

东方博宜1317 - 正多边形每个内角的度数?

问题描述 根据多边形内角和定理&#xff0c;正多边形内角和等于&#xff1a;&#xff08; n&#xff0d;2 &#xff09; 180∘( n 大于等于 3 且 n 为整数&#xff09;。 请根据正多边形的边数&#xff0c;计算该正多边形每个内角的度数。&#xff08;结果保留1位小数&#x…

Python 机器学习 基础 之 【实战案例】新闻内容分类实战

Python 机器学习 基础 之 【实战案例】新闻内容分类实战 目录 Python 机器学习 基础 之 【实战案例】新闻内容分类实战 一、简单介绍 二、新闻内容分类实战 三、数据准备 四、分词与清洗工作 1、读取数据 2、使用 jieba 分词器进行分词&#xff0c;使用 Pandas 创建Data…

cesium已知两个点 写一个简单具有动画尾迹效果的抛物线

// 定义起点和终点的经纬度和高度 var start = {longitude: 111.09683723811149, latitude: 38.92112250636146, elevation: 603.5831692856873 }; var end = {longitude: 111.09769465526689, latitude: 38.92815375977821, elevation: 627.0132157062261 }; // 生成更多的中…

【亲测可用】Linux的wget太慢,使用mwget加速下载

微信公众号&#xff1a;leetcode_algos_life&#xff0c;代码随想随记 小红书&#xff1a;412408155 CSDN&#xff1a;https://blog.csdn.net/woai8339?typeblog &#xff0c;代码随想随记 GitHub: https://github.com/riverind 抖音【暂未开始&#xff0c;计划开始】&#xf…

R语言探索与分析19-CPI的分析和研究

一、选题背景 CPI&#xff08;居民消费价格指数&#xff09;作为一个重要的宏观经济指标&#xff0c;扮演着评估通货膨胀和居民生活水平的关键角色。在湖北省这个经济活跃的地区&#xff0c;CPI的波动对于居民生活、企业经营以及政府宏观经济政策制定都具有重要的影响。因此&a…

ES 面试手册

Elasticsearch是什么&#xff1f; Elasticsearch是一个基于Lucene的搜索和分析引擎&#xff0c;它提供了一个分布式、多租户能力的全文搜索引擎&#xff0c;具有HTTP Web界面和无模式JSON文档。 Elasticsearch中的倒排索引是什么&#xff1f; 倒排索引是搜索引擎的核心结构&a…