数据结构——哈夫曼树及其应用(哈夫曼编码)

news2024/10/18 5:21:27

判断树:用来描述分类过程的二叉树

哈夫曼树(最优二叉树)的基本概念

路径:从树中一个结点到另一个结点之间的分支构成这两个结点间的路径。

结点的路径长度:两结点间路径上的分支数

结点的路径长度计算:

 树的路径长度:从树根到每一个结点的路径长度之和。记作TL

示例:

 结点数目相同的二叉树中,完全二叉树是路径长度最短的二叉树。

权(weight):将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权

 结点的带权路径长度:

从根结点到该结点之间的路径长度与该结点的权的乘积。

树的带权路径长度:树中所有叶子结点的带权路径长度之和。(要与树的路径长度区分开来,树的路径长度是所有结点的路径长度之和,而树的带权路径长度则是所有叶子结点的带权路径长度之和)

 可知,相同的叶子结点数,相同的权值,数的形态不同,树的带权路径长度是不一样的。

 哈夫曼树:最优树【带权路径长度(WPL)最短的树】

注:“带权路径长度最短”是在“度相同”的树中比较而得的结果,因此有最优二叉树、最优三叉树之称等等。

最优二叉树

带权路径长度最短的二叉树。构造哈夫曼树的算法称为哈夫曼算法。

满二叉树不一定是哈夫曼树

由上图可以看出,权值比较小的结点离根结点比较远,权值比较大的离根结点比较近,具有相同带权结点的哈夫曼树不唯一。

构造哈夫曼树(哈夫曼算法)

(1)根据n个给定的权值{W1,W2,...,Wn}构成n棵二叉树的森林,F={T1,T2,...,Tn},其中Ti只有一个带权为wi的根结点。

  • 构造森林全是根

(2)在F中选取两棵结点的权值最小的树作为左右子树,构造一棵新的二叉树,且设置新的二叉树的根结点的权值为其左右子树上根结点的权值之和。

  • 选用两小造新树

(3)在F中删除这两棵树,同时将新得到的二叉树加入到森林中。

  • 删除两小添新人

(4)重复(2)和(3),直到森林中只有一棵树为止,这棵树即为哈夫曼树。

  • 重复2、3剩单根

哈夫曼树的特点是,权重越大的节结点位于离根结点越近的位置,哈夫曼树的结点的度数为0或2,没有度为1的结点;包含n个n叶子结点的哈夫曼树中共有2n-1个结点(包含n棵树的森林要经过n-1次合并才能形成哈夫曼树,共产生n-1个新结点)。

哈夫曼树构造算法的实现

采用顺序存储结构——一维结构数组 HuffmanTree H;(叶子结点没有左右孩子,根结点没有双亲)

 结点类型定义

typedef struct {
	int weight;
	int parent, lch, rch;
}HTNode,*HuffmanTree;

例如:第一个结点权值为5,即可表示为H[i].weight = 5;

例如:有n=8,权值为W={7,19,2,6,32,3,21,10},构造哈夫曼树

1、初始化HT[1......2n-1]:lch=rch=parent=0

2、输入初始n个叶子结点:置HT[1.......n]的weight值; 

代码实现:

void CreateHuffmanTree(HuffmanTree HT, int n)
{
	if (n <= 1)return;
	m = 2 * n - 1;//数组共2n-1个元素
	HT = new HTNode[m + 1];//0号单元未用,HT[m]表示根结点
	for (i = 0; i < m; ++i)//将2n-1个元素的lch、rch、parent置为0
	{
		HT[i].lch = 0;
		HT[i].rch = 0;
		HT[i].parent = 0;
	}
	for (i = 1; i <= n; i++)cin >> HT[i].weight;//输入前n个元素的weight值
	//初始化结束,下面开始建立哈夫曼树
}

 3、进行以下n-1次合并,依次产生n-1个结点HT[i],i=n+1,......,2n-1;

a)在HT[1..i-1]中选两个未被选过(从parent ==0 的结点中选)的weight最小的两个结点HT[s1]和HT[s2],s1、s2为两个最小结点下标

b)修改HT[s1]和HT[s2]的parent值:HT[s1].parent=i; HT[s2].parent=i

c)修改新产生的HT[i]:

  • HT[i].weight=HT[s1].weight + HT[s2].weight;
  • HT[i].lch=s1; HTi. rch=s2;

代码实现:

for (i = n + 1; i <= m; i++)//合并产生n-1个结点——构造哈夫曼树(Huffman树)
{
	Select(HT,i-1,s1,s2);//在HT[k](1<=k<=i-1)中选择两个双亲域为0,
						 //且权值最小的结点,并返回他们在HT中的序号s1和s2
	HT[s1].parent = i; HT[s2].parent = i;//表示从F中删除s1,s2
	HT[i].lch = s1; HT[i].rch = s2;//s1,s2分别作为i的左右孩子
	HT[i].weight = HT[s1].weight + HT[s2].weight;//i的权值为左右孩子权值之和
}

什么是哈夫曼编码

若将编码设计为长度不等的二进制编码,即让待传字符串中出现次数较多的字符采用尽可能短的编码,则转换的二进制字符串便可能减少。 

 

 关键:要设计长度不等的编码,则必须使任一字符的编码都不是另一个字符的编码的前缀。

 (这种编码称为前缀编码

问题:什么样的前缀编码能使得电文总长最短?

        ——哈夫曼编码

1、统计字符集中每个字符在电文中出现的平均概率(概率越大,要求编码越短)。

2、利用哈夫曼树的特点:权越大的叶子离根越近;将每个字符的概率值作为权值,构造哈夫曼树。则概率越大的结点,路径越短。

3、在哈夫曼树的每个分支上标上0或1:结点的左分支标 0,右分支标1,把从根到每个叶子的路径上的标号连接起来,作为该叶子代表的字符的编码。

【例 】 要传输的字符集 D = {C,A,S,T,;}

        字符出现频率w={2,4,2,3,3 }

构造哈夫曼树,如下:

 左分支标记0,右分支标记1,如下:

从根结点出发,路过的分支连起来,作为字符的编码,这个编码就叫做哈夫曼编码,如下:

 

 例电文是{CAS;CAT:SAT;AT}

其编码是:11010111011101000011111000011000

反之,若编码是1101000,则电文为CAT

 问题:

1、为什么哈夫曼编码能够保证是前缀编码?

因为没有一片树叶是另一片树叶的祖先,所以每一个叶子结点的编码就不可能是其他叶子结点的前缀

2、为什么哈夫曼编码能够保证字符编码总长最短?

因为哈夫曼树的带权路径长度最短,故字符编码的总长最短。

  • 性质1:哈夫曼编码是前缀码
  • 性质2:哈夫曼编码是最优前缀码

哈夫曼编码的算法实现

void CreateHuffmanCode(HuffmanTree HT, HuffmanCode& HC, int n) {
	//从叶子到根逆向求每个字符的哈夫曼编码,存储在编码表HC中
	HC = new char* [n + 1]; //分配n个字符编码的头指针矢量
	cd = new char[n];//分配临时存放编码的动态数组空间
	cd[n - 1] = '\0';//编码结束符
	for (i = 1; i <= n; ++i) {
		start = n - 1; c = i; f = HT[i].parent;
		while (f != 0) {//从叶子结点开始向上溯源,知道根结点,没有双亲
			--start;  //回溯一次start向前指一个位置
			if (HT[f].lchild == c)cd[start] = '0';//结点c是f的左孩子,则生成代码0
			else                  cd[start] = '1';//结点c是f的右孩子,则生成代码1
			c = f; f = HT[f].parent;//继续向上溯源
		}
		HC[i] = new char[n - start];//为第i个字符串编码分配空间
		strcpy(HC[i], &cd[start]);//将求得的编码从临时空间cd分配到HC的当前行中
	}
	delete cd; //释放临时空间
}

文件的编码和解码

1.编码:

  • 输入各字符及其权值
  • 构造哈夫曼树--HT[i]
  • 进行哈夫曼编码--HC[i]
  • 查HC],得到各字符的哈夫曼编码 

 2.解码:

  • 构造哈夫曼树依次读入二进制码读入0,则走向左孩子;
  • 读入1,则走向右孩子
  • 一旦到达某叶子时,即可译出字符
  • 然后再从根出发继续译码,指导结束 

 解码示例:

收到如下编码:

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

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

相关文章

【jQuery】jQuery 处理 Ajax 以及解决跨域问题的方式

文章目录 HTTP原生创建 AjaxjQuery 处理 Ajax$.ajax()$().load()$.get()$.post() 跨域CORSJSONPiframeweb sockets HTTP 超文本传输协议&#xff08;HTTP&#xff0c;HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。设计 HTTP 最初的目的是为了提供一种发…

Redis中String类型数据扩容原理分析

大家好&#xff0c;我是 V 哥。在 Java 中&#xff0c;我们有动态数组ArrayList&#xff0c;当插入新元素空间不足时&#xff0c;会进行扩容&#xff0c;好奇 Redis 中的 String 类型&#xff0c;C 语言又是怎样的实现策略&#xff0c;带着疑问&#xff0c;咱们来了解一下。 最…

集成电路公司进销存系统生成合同——未来之窗行业应用跨平台架构

一、进销存生成合同优势 1. 提高效率&#xff1a;节省了手动起草和编辑合同的时间&#xff0c;能够快速生成合同&#xff0c;加快业务流程。 2. 减少错误&#xff1a;避免了人工输入可能导致的拼写、数据错误等&#xff0c;提高合同的准确性和规范性。 3. 数据一致性&#xff…

Python Web服务器网关接口

gunicorn 是 WSGI。 因其中一个项目说是要用 gunicorn &#xff0c;然后就顺便了解下 gunicorn 这个东西是干什么的。 要想了解 gunicorn &#xff0c;那么就需要知道 WSGI 是什么东西。 开始都不知道 WSGI 是什么概念&#xff0c;还以为是个新东西。 其实就是 Python 实现…

通过Caffeine实现JVM进程缓存、配置OpenResty完成nginx的本地缓存和redis操作,Canal实现缓存同步——配置多级缓存,一篇足矣

目录 JVM缓存&#xff08;本地进程缓存&#xff09;Caffeine技术栈基础介绍&#xff1a; OpenResty技术栈基础介绍 Canal技术栈介绍 一、通过Caffeine实现进程缓存 1.1首先需要为你需要的数据构建Cache缓存对象&#xff0c;为了方便使用&#xff0c;可以将其声明为一个bea…

Android 13.0 Launcher3定制之首页时钟小部件字体大小修改

1.前言 在13.0的系统rom产品开发中,在一些Launcher3的定制化开发中,在对于一些小屏幕的产品开发中,在首页添加时钟小部件会显得字体有点小, 所以为了整体布局美观就需要改动小部件的布局日期字体的大小来实现整体的布局美观效果,接下来来具体实现相关的功能 具体效果图: …

linux 修改主机名和用户名颜色

编译 ~/.bashrc vim ~/.bashrc 如下格式 PS1\[\e[1;31m\]\h:\[\e[0;32m\]\w \[\e[1;34m\]\u\[\e[0m\]\$ 颜色随自己喜好修改 如下使其生效 source ~/.bashrc 效果如下 Enjoy&#xff01;&#xff01;&#xff01;

数学考研错题本:查漏补缺,高效提升备考策略

考研之路漫长而艰辛&#xff0c;对于数学这一学科来说&#xff0c;错题本的建立与利用显得尤为重要&#xff0c;通过分析错题&#xff0c;我们可以查漏补缺&#xff0c;找到自己的薄弱环节&#xff0c;从而有针对性地进行复习&#xff0c;本文将详细阐述如何建立和利用数学考研…

【数据采集工具】Sqoop从入门到面试学习总结

国科大学习生活&#xff08;期末复习资料、课程大作业解析、大厂实习经验心得等&#xff09;: 文章专栏&#xff08;点击跳转&#xff09; 大数据开发学习文档&#xff08;分布式文件系统的实现&#xff0c;大数据生态圈学习文档等&#xff09;: 文章专栏&#xff08;点击跳转&…

数控机械制造工厂ERP适用范围有哪些

在当今制造业高速发展的背景下&#xff0c;企业资源计划(ERP)系统已成为提升工厂管理效率、实现生产自动化与信息化的关键工具。特别是对于数控机械制造工厂而言&#xff0c;一个合适的ERP系统能够帮助其优化生产流程、提高产品质量、降低生产成本并增强市场竞争力。 1. 生产计…

React 项目热更新失效问题的解决方案和产生的原因

背景和意义 在修复React项目热更新失效的问题时&#xff0c;经过一系列问题排查和依赖升级&#xff0c;最终成功修复了问题并为后续开发规避了类似的问题。 依赖升级 Vite版本升级 原React项目Vite版本升级到^4.4.5 Vite 4 在构建和开发服务器的性能上进行了优化&#xff…

Java--集合之vectorlinkedlisthashset结构

文章目录 0.架构图1.vector解析2.LinkedList分析2.1源码分析2.2迭代器遍历的三种方式 3.set接口的使用方法3.1基本使用说明3.2基本遍历方式3.3HashSet引入3.4数组链表模拟3.5hashset扩容机制3.6hashset源码解读3.7扩容*转成红黑树机制**我的理解 0.架构图 1.vector解析 和之前介…

15分钟学Go 第4天:Go的基本语法

第4天&#xff1a;基本语法 在这一部分&#xff0c;将讨论Go语言的基本语法&#xff0c;了解其程序结构和基础语句。这将为我们后续的学习打下坚实的基础。 1. Go语言程序结构 Go语言程序的结构相对简单&#xff0c;主要包括&#xff1a; 包声明导入语句函数语句 1.1 包声…

物联网协议:MQTT、CoAP 和 LwM2M 的比较与应用

目录标题 1.引言 &#x1f4d8;2. 物联网协议概述 &#x1f4da;3. MQTT 协议详解 &#x1f4e1;&#x1f4e1;&#x1f4e1;&#x1f4e1;3.1 协议特点3.2 工作原理3.3 应用场景 4. CoAP 协议详解 &#x1f517;4.1 协议特点4.2 工作原理4.3 应用场景 5. LwM2M 协议详解 ⚙️5…

LeetCode刷题日记之贪心算法(一)

目录 前言分发饼干摆动序列最大子数组和总结 前言 作为LeetCode刷题的过程中&#xff0c;贪心算法一直是一个经典的算法思路。在这篇文章中&#xff0c;我将记录自己刷LeetCode贪心算法题的过程&#xff0c;并逐步梳理该算法的核心逻辑&#xff0c;包括如何选择最优解、判断局…

PS证件照换底色

ps工具&#xff1a;Adobe Photoshop 2021 文章目录 1. 扣取人物2. 更换底色 1. 扣取人物 2. 更换底色

SpringSecurity使用介绍

1、SpringSecurity 1.1 SpringSecurity简介 Spring Security是基于Spring的安全框架,提供了包含认证和授权的落地方案&#xff1b;Spring Security底层充分利用了Spring IOC和AOP功能&#xff0c;为企业应用系统提供了声明式安全访问控制解决方案&#xff1b;SpringSecurity可…

数据字典是什么?和数据库、数据仓库有什么关系?

一、数据字典的定义及作用 数据字典是一种对数据的定义和描述的集合&#xff0c;它包含了数据的名称、类型、长度、取值范围、业务含义、数据来源等详细信息。 数据字典的主要作用如下&#xff1a; 1. 对于数据开发者来说&#xff0c;数据字典包含了关于数据结构和内容的清晰…

3. 单例模式唯一性问题—构造函数

1. 构造函数带来的唯一性问题指什么&#xff1f; 对于不继承MonoBehaviour的单例模式基类 我们要避免在外部 new 单例模式类对象 例如 &#xff08;完整单例模式定义在上一节&#xff09; public class Main : MonoBehaviour {void Start(){// 破坏单例模式的唯一性&#xf…

跨越距离:2024四大远程控制软件体验!

在多元化的现代生活中&#xff0c;远程控制软件已经成为我们不可或缺的助手。它们可以帮助我们实现远程办公、远程协助、远程游戏等多种功能。今天&#xff0c;我们就来为大家盘点几款热门的远程控制软件&#xff0c;包括向日葵远程控制、RayLink远程控制、Parsec和AirDroid&am…