数据结构之最优二叉树

数据结构之最优二叉树

  • 1、最优二叉树
  • 2、哈夫曼编码

  数据结构是程序设计的重要基础,它所讨论的内容和技术对从事软件项目的开发有重要作用。学习数据结构要达到的目标是学会从问题出发,分析和研究计算机加工的数据的特性,以便为应用所涉及的数据选择适当的逻辑结构、存储结构及其相应的操作方法,为提高利用计算机解决问题的效率服务。
  数据结构是指数据元素的集合及元素间的相互关系和构造方法。元素之间的相互关系是数据的 逻辑结构,数据元素及元素之间关系的存储称为 存储结构(或物理结构)。数据结构按照逻辑关系的不同分为 线性结构非线性结构两大类,其中,非线性结构又可分为树结构和图结构。
  树结构是一种非常重要的非线性结构,该结构中的一个数据元素可以有两个或两个以上的直接后继元素,树可以用来描述客观世界中广泛存在的层次结构关系。
  二叉树是 n(n≥0)个结点的有限集合,它或者是空树(n=0),或者是由一个根结点及两棵不相交的且分别称为左、右子树的二叉树所组成。

1、最优二叉树

  最优二叉树又称为哈夫曼树,它是一类带权路径长度最短的树。路径是从树中一个结点到另一个结点之间的通路,路径上的分支数目称为路径长度。
  树的路径长度是从树根到每一个叶子之间的路径长度之和。结点的带权路径长度为从该结点到树根之间的路径长度与该结点权值的乘积。
  树的带权路径长度为树中所有叶子结点的带权路径长度之和,记为
W P L = Σ k = 1 n w k l k WPL={\huge\Sigma}^n_{k=1}{\large w}_k {\large l}_k WPL=Σk=1nwklk
其中,n 为带权叶子结点数目,wk 为叶子结点的权值,lk为叶子结点到根的路径长度。
  哈夫曼树是指权值为 w1,w2,···,wn 的n个叶子结点的二又树中带权路径长度最小的二叉树。
  例如,下图所示的具有4个叶子结点的二叉树,其中以图 (b) 所示的二叉树带权路径长度最小。
在这里插入图片描述

  那么如何构造最优二叉树呢? 构造最优二叉树的哈夫曼算法如下。
  (1)根据给定的n个权值 {w1,w2,···,wn},构成n颗二叉树的集合F= (T1,T2,···,Tn},其中,每棵树 Ti 中只有一个带权为 wi 的根结点,其左、右子树均空。
  (2)在F中选取两棵权值最小的树作为左、右子树构造一棵新的二叉树,置新构造二叉树的根结点的权值为其左、右子树根结点的权值之和。
  (3)从F中删除这两棵树,同时将新得到的二叉树加入到F中。
  重复(2)、(3) 步,直到 F 中只含一棵树时为止,这棵树便是最优二叉树(哈夫曼树)。
  由此算法可知,以选中的两棵子树构成新的二叉树,哪个作为左子树,哪个作为右子树,并没有明确。所以,具有n 个叶子结点的权值为 w1,w2,···,wn 的最优二叉树不唯一,但其 WPL 值是唯一确定的。
  当给定了 n 个权值后,构造出的最优二叉树中的结点数目 m 就确定了,即 m=2×n-1,所以可用一维的结构数组来存储最优二叉树,下面举例说明。

#define MAXLEAFNUM 50						/*最优二叉树中的最多叶子数目*/
typedef struct nodef{
	char ch;								/*结点表示的字符,对于非叶子结点,此域不用*/
	int weight;								/*结点的权值*/
	int parent;								/*结点的父结点的下标,为0时表示无父结点*/
	int lchild,rchild;						/*结点的左、右孩子结点的下标,为0 时表示无孩子结点*/
}HuffmanTree[2*MAXLEAFNUM];
typedef char* HuffmanCode[MAXLEAFNUM+1];

  【函数】创建最优二叉树。

void createHTree(HuffmanTree HT, char *c, int *w,int n)
/*数组 c[0..n-1]和 w[0..n-1]存放了n个字符及其概率,构造哈夫曼树HT*/
{
	int i,sl,s2;
	if (n <= 1) return;
	for(i=l; i<-n; i++){ /*根据n个权值构造n只有根结点的二叉树*/
		HT[i].ch = c[i-l]; 
		HT[i].weight = w[i-l];
		HT[i].parent=HT[i].lchild=HT[i].rchild=0;
	}
	for(;i<2*n;++i) { /*初始化*/
		HT[i].parent=0;
		HT[i].lchild=0; 
		HT[i].rchild=0;
	}
	for(i= n+l; i<2*n; i++)
		/*从HT[l..i-1]中选择 parent 为0且weight最小的两棵树,其序号为 s1和s2*/
		select(HT,i-1,s1,s2);
		HT[sl].parent = i;
		HT[s2].parent = i;
		HT[i].lchild = s1;
		HT[i].rchild = s2;
		HT[i].weight = HT[sl].weight + HT[s2].weight;
	}/*for*/
}/* createHTree*/

2、哈夫曼编码

  若对每个字符编制相同长度的二进制码,则称为等长编码。例如,英文字符集中的 26个字符可采用5 位二进制位串表示,按等长编码格式构造一个字符编码表。发送方按照编码表对信息原文进行编码后送出电文,接收方对接收到的二进制代码按每 5位一组进行分割,通过字符的编码表即可得到对应字符,实现译码。
  等长编码方案的实现方法比较简单,但对通信中的原文进行编码后,所得电文的码串过长不利于提高通信效率,因此希望缩短码串的总长度。如果对每个字符设计长度不等的编码,且让电文中出现次数较多的字符采用尽可能短的编码,那么传送的电文码串总长度则可减少。
  如果要设计长度不等的编码,必须满足下面的条件:任一字符的编码都不是另一个字符的编码的前缀,这种编码也称为前缀码。对给定的字符集 D={d1,d2,···,dn} 及字符的使用频率W={w1,w2,···,wn},构造其最优前缀码的方法为: 以d1,d2,···,dn 作为叶子结点,w1,w2,···,wn 作为叶子结点的权值,构造出一棵最优二叉树,然后将树中每个结点的左分支标上 0,右分支标上1,则每个叶子结点代表的字符的编码就是从根到叶子的路径上的0、1组成的串。
  例如,设有字符集{a,b,c,d,e}及对应的权值集合{0.30,0.25,0.15,0.22,0.08},按照构造最优二叉树的哈夫曼方法:先取字符 c和e所对应的结点构造一棵二叉树(根结点的权值为c和e的权值之和),然后与 d 对应的结点分别作为左、右子树构造二叉树,之后选a 和b所对应的结点作为左、右子树构造二叉树,最后得到的最优二叉树 (哈夫曼树)如下图所示。其中,字符a的编码为 00,字符b、c、d、e的编码分别为 01、100、11、101。
前缀编码示例

  译码时就从树根开始,若编码序列中当前编码为 0,则进入当前结点的左子树;为1 则进入右子树,到达叶子时一个字符就翻译出来了,然后再从树根开始重复上述过程,直到编码序列结束。例如,若编码序列101110000100 对应的字符编码采用上图所示的树进行构造,则可翻译出字符序列"edaac"。
  【函数】根据给定的哈夫曼树,从每个叶子结点出发追溯到树根,逆向找出最优二叉树中叶子结点的编码。

void HuffmanCoding(HuffmanTree HT, HuffmanCode HC,int n)
/*n个叶子结点在哈夫曼树HT中的下标为 1~n,第i(l≤i≤n)个叶子的编码存放HC[i]中*/
{
	char *cd; 
	int i, start, c, f;
	if(n <= 1) return;
	cd =(char *)malloc(n*sizeof(char));
	cd[n-1]='\0';
	for(i= l; i<= n;+){
		start = n-l;
		for(c = i,f= HT[i],parent; f!=0; c=f,f=HT[f].parent)
			if (HT[f].lchild == c) cd[--start] = '0';
			else cd[--start]='1';
		HC[i] =(char*)malloc((n-start)*sizeof(char));
		strcpy(HC[il,&cd[start]);
	}/*for*/
	free(cd);
}/*HuffmanCoding*/

  利用哈夫曼树译码的过程为:从根结点出发,按二进制位串中的0和1确定是进入左分支还是右分支,当到达叶子结点时译出一个字符。若位串未结束,则回到根结点继续上述译码过程,直到位串结束。
  【函数】用最优二又树进行译码

void Decoding(HuffmanTree HT,int n,char *buf) 
/*利用具有n个叶子结点的最优二叉树(存储在数组 HT 中) 进行译码,叶子的下标为 1~n*/
/*buff 指向二进制位串编码序列*/
{
	int p=2*n-1;
	while (*buff){
		if((*buff) == '0') p = HT[p].lchild;		/*进入左分支*/
		else p = HT[p].rchild;						/*进入右分支*/
		if(HT[p].lchild==0 && HT[p].rchild==0){		/*到达一个叶子结点*/
			printf("%c", HT[p].ch);
			p=2*n-1;								/*回到树根*/
		}/*if*/
		buff++;
	}/*while*/
}/*Decoding*/

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

分辨率 时钟频率 lane速率计算

PCLK: pixel clock(像素频率) 计算方法如下&#xff1a; 以1920x1080p/60hz为例&#xff0c;total pixel&#xff1a;2200&#xff0c;total line&#xff1a;1125&#xff0c;filed rate&#xff1a;60Hz&#xff0c;那么&#xff1a;PCLK 2200*1125*60 148.5MHz&#xff1b…

Vue-35、Vue中使用ref属性

1、ref属性 2、代码 <template><div id"app"> <!-- <img alt"Vue logo" src"./assets/logo.png">--><h1 v-text"msg" ref"title"></h1><button click"showDOM" ref&…

gdzwfw某省公共资源交易平台逆向学习

声明&#xff1a;本文中网站仅为学习技术使用&#xff0c;请勿暴力爬取数据。 学习地址&#xff1a;aHR0cHM6Ly95Z3AuZ2R6d2Z3Lmdvdi5jbi8jLzQ0L2p5Z2c 此网站采用请求头反爬&#xff0c;难点是请求头中几个参数是如何生成的&#xff08;别问为什么知道是请求头&#xff0c;一…

推荐系统算法 协同过滤算法详解(二)皮尔森相关系数

目录 前言 协同过滤算法(简称CF) 皮尔森(pearson)相关系数公式 算法介绍 算法示例1&#xff1a; 算法示例2 前言 理解吧同胞们&#xff0c;实在是没办发把wps公式复制到文章上&#xff0c;只能截图了&#xff0c;我服了&#xff01;&#xff01;&#xff01; 协同过滤算法…

pytestallure分析redis的数据并动态生成testCase报告

1.pytest.mark.parametrize pytest.mark.parametrize 是一个pytest的装饰器&#xff0c;它可以用于将参数传递给测试函数。使用 pytest.mark.parametrize 装饰器时&#xff0c;需要在装饰器中指定参数名称和参数值。对于多个参数&#xff0c;可以使用多个装饰器。 下面是一些…

ZXing开源库生成二维码

引言 二维码&#xff08;QR Code&#xff09;作为一种快速、高容量、高密度的矩阵条码&#xff0c;已经在各行各业得到广泛应用。ZXing&#xff08;Zebra Crossing&#xff09;是一款由Google开源的Java二维码生成和解析库&#xff0c;提供了丰富的功能和易于使用的API。本篇博…

sqlmap使用教程(5)-信息获取

MySQL数据库 -b&#xff0c;用来获取数据库标识 --hostname&#xff0c;可以获取数据库服务器的主机名 -current-user&#xff0c;用来获取当前连接数据库的用户名 --users&#xff0c;获取数据库管理系统中的所有用户 --passwords&#xff0c;可以获取数据库用户密码的哈希值…

接口文档管理工具(yapi的使用)

文章目录 一、API管理工具二、yapi 接口管理工具功能权限管理项目管理页面功能接口接口创建接口配置管理界面 动态成员管理数据管理设置 三、 docker安装yapi三、使用流程四、参考 一、API管理工具 [推荐-官方描述]使用 YApi 管理 API 文档&#xff0c;测试&#xff0c; mock …

【C++干货铺】 RAII实现智能指针

个人主页点击直达&#xff1a;小白不是程序媛 C系列专栏&#xff1a;C干货铺 代码仓库&#xff1a;Gitee 目录 为什么需要智能指针&#xff1f; 内存泄漏 什么是内存泄漏&#xff0c;内存泄露的危害 内存泄漏的分类 堆内存泄漏&#xff08;Heap leak&#xff09; 系统资…

LIO-SAM 论文阅读

论文链接 LIO-SAM 0. Abstract 提出了一种通过平滑和映射进行紧耦合激光雷达惯性里程计的框架 LIO-SAM&#xff0c;它实现了高精度、实时的移动机器人轨迹估计和地图构建 LIO-SAM 在因子图上制定激光雷达惯性里程计&#xff0c;允许将多种相对和绝对测量&#xff08;包括闭环…

k8s-基础知识(Pod,Deployment,ReplicaSet)

k8s职责 自动化容器部署和复制随时扩展或收缩容器容器分组group&#xff0c;并且提供容器间的负载均衡实时监控&#xff0c;即时故障发现&#xff0c;自动替换 k8s概念及架构 pod pod是容器的容器&#xff0c;可以包含多个container pod是k8s最小可部署单元&#xff0c;容器…

理解分布式存储的真实成本 - 10PB的硬件和软件

我们最近与一家大型银行的首席信息官进行了一次对话。他们是全球系统性重要银行之一——规模极其庞大。这位CIO决定将MinIO引入为数据分析计划的对象存储。这个部署从抵押贷款、交易和新闻平台收集数据&#xff0c;以运行Spark和其他分析工具&#xff0c;为银行提供洞察力。Min…

【C语言】【插入排序】

void InsertSort(int* a, int n) {int end 0, tmp 0;for (int i 0;i < n - 1;i){end i;tmp a[end 1];while (end > 0){if (a[end] > tmp){a[end 1] a[end];--end;}elsebreak;}a[end 1] tmp;} }逻辑解释&#xff1a; 变量end代表某次循环&#xff0c;要比较…

学习笔记-李沐动手学深度学习(三)(10-11,隐藏层、多层感知机、激活函数、模型超参数选择、欠过拟合)

总结 多体会&#xff08;宏观、哲学&#xff09; 【深度学习的核心】首先是要模型足够大&#xff0c;在此基础上通过各种手段 来控制模型容量&#xff0c;使得最终得到较小的泛化误差 【一般深度学习特指神经网络 这一块】 【学习的核心是要学习 本质上不变的那些核心思想&a…

MySql必知必会

41.undo log、redo log、 bin log的作用是什么&#xff1f; undo log 基本概念 undo log是一种用于撤销回退的日志&#xff0c;在数据库事务开始之前&#xff0c;MySQL会先记录更新前的数据到 undo log日志文件里面&#xff0c;当事务回滚时或者数据库崩溃时&#xff0c;可以…

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK使用Binning像素合并功能(C#)

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK使用Binning像素合并功能&#xff08;C#&#xff09; Baumer工业相机Baumer工业相机NEOAPI SDK和短曝光功能的技术背景Baumer工业相机通过CameraExplorer软件使用Binning功能Baumer工业相机通过NEOAPI SDK使用Binning功能1.引用合…

基于禁忌搜索算法的TSP路径规划matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1 TSP问题描述 4.2 禁忌搜索算法原理 4.3 算法步骤 5.完整程序 1.程序功能描述 基于禁忌搜索算法的TSP路径规划,输出优化收敛曲线以及路线规划图。 2.测试软件版本以及运行结果展示 …

芯课堂 | 通过ISP升级芯片固件方法及框架

一、升级原理 芯片在应用前&#xff0c;是一颗裸片&#xff0c;内部没有任何驱动或应用程序。芯片在贴上PCB板子后&#xff0c;会实现各种功能&#xff0c;这是时候会开发对应的驱动或者应用程序&#xff0c;在芯片上面运行的程序&#xff0c;一般称之为固件&#xff08;Firmw…

线程池高手进阶:揭秘ThreadPoolExecutor的小妙招!

RejectedExecutionHandler总结 ThreadPoolExecutor 是 Java 中用于创建和管理线程池的接口&#xff0c;当线程池中的任务队列已满&#xff0c;并且线程池中的线程数量已经达到最大时&#xff0c;如果再有新的任务提交&#xff0c;就需要一个策略来处理这些无法执行的任务。它 …

antd 日期选择框增加季度预设范围

测试同学说想要有个季度的预设选择框&#xff0c;方便快速选择季度的开始和结束日期。 antd 的rangepicker是支持预设的 日期选择框 DatePicker - Ant Design 实现方法很简单&#xff0c;按照官网示例用moment初始化一下即可 获取当前一季度的开始日期时间&#xff1a; mom…