[入门必看]数据结构5.5:树与二叉树的应用

news/2024/7/15 0:25:31 标签: 数据结构, 算法, 图论

[入门必看]数据结构5.5:树与二叉树的应用

  • 第五章 树与二叉树
  • 5.5 树与二叉树的应用
    • 知识总览
      • 5.5.1 哈夫曼树
      • 5.5.2_1 并查集
      • 5.5.2_2 并查集的进一步优化
    • 5.5.1 哈夫曼树
      • 带权路径长度
      • 哈夫曼树的定义
      • 哈夫曼树的构造
      • 哈夫曼编码
      • 应用:英文字母频次
    • 5.5.2_1 并查集
      • 漏网之鱼:逻辑结构——“集合”
      • 回顾:森林
      • 回忆:树的存储——双亲表示法
      • “并查集”的存储结构
      • “并查集”的基本操作
      • “并查集”的代码实现——初始化
      • “并查集”的代码实现——并、查
      • 时间复杂度分析
      • Union操作的优化
    • 5.5.2_2 并查集的进一步优化
      • 拓展:Find 操作的优化(压缩路径)
      • 并查集的优化
      • 快乐时刻
  • 知识回顾与重要考点
    • 5.5.1 哈夫曼树
    • 5.5.2_1 并查集
    • 5.5.2_2 并查集的进一步优化


第五章 树与二叉树

小题考频:30
大题考频:8


5.5 树与二叉树的应用

难度:☆☆☆☆

知识总览

5.5.1 哈夫曼树

在这里插入图片描述

5.5.2_1 并查集

在这里插入图片描述

5.5.2_2 并查集的进一步优化

——补充:并查集的终极优化


5.5.1 哈夫曼树

在这里插入图片描述

带权路径长度

在这里插入图片描述

带权路径长度:边数✖结点上的权值
Eg.在这里插入图片描述

树的带权路径长度:所有的叶子结点带权路径长度之和(WPL)

哈夫曼树的定义

在这里插入图片描述

只算叶子结点,带权路径长度之和
哈夫曼树WPL最小,也称最优二叉树
上图中中间两棵树的WPL=25为最小,那么这两棵树即哈夫曼树。

哈夫曼树的构造

在这里插入图片描述

  1. 选权值最小的两个结点让他们成为兄弟。
    Eg. 选a结点和c结点
  2. 把这两个结点的权值之和作为新树根结点的权值,然后继续选。
    Eg.新树为权值为3,选新树和e

重复上述操作。

哈夫曼树的一些性质:

  1. 每个初始结点最终都成为叶结点,且权值越小的结点到根结点的路径长度越大
  2. 两两结合需要进行n-1次,每结合一次会增加一个分支结点,哈夫曼树的结点总数为2n-1
  3. 哈夫曼树中不存在度为1的结点。
  4. 哈夫曼树并不唯一,但是WPL相同且最优。

Eg.2 另一种构造方法:
在这里插入图片描述
哈夫曼树的特性有什么用呢?


哈夫曼编码

发电报——点、划两个信号(二进制0/1)
在这里插入图片描述

如果采用ASCII编码,两人想传递100个答案,就需要操作8×100=800次,显然不高效。
字符集中只有四个字符,用两个二进制位就可以区分出四种状态:在这里插入图片描述
也属于固定长度编码,每个字符的二进制位长度相同。

80个C、10个A、8个B、2个D,可以映射成树,向左走是0,向右走是1:
在这里插入图片描述
那么所有答案的二进制长度就是最终的WPL=200,即操作200次就可以把答案传出去。

有没有更好的方案?
也就是说,让他们之间传递的二进制比特信息尽可能的少。
即构造的编码树的带权路径长度尽可能的小。

即构造哈夫曼树:

权值最小的是D和B,让其合并,接下来是A,最后是C

在这里插入图片描述

也就是说只要操作130次,就可以把答案传递出去了。

此处各字符对应的二进制长度不相同,这种编码方式就叫可变长度编码。

Q:此处不就是想用四个二进制串,来区分四个字符吗?字符A出现的频率也高,为什么非要用两个二进制位来表示A,而不用1来表示呢?这样也可以保证四个字符的二进制编码不一样。

在这里插入图片描述
A:用1来表示A,在树的形式中,A就变成了一个非叶子结点:

在这里插入图片描述

此时想要传递 CAAABD答案,就变成了0 111 111 110,接受这一段二进制数之后,要把信息翻译成A、B、C、D,就会变成C(0)、B(111)、B(111)、D(110),那么就出现了歧义:
在这里插入图片描述
如果用左边的编码方案,可以得到正确结果:加粗样式

左边这种编码方式可以称为:前缀编码

任何一个编码都不是另一个编码的前缀——无歧义

在这里插入图片描述
有哈夫曼树得到哈夫曼编码——字符集中的每个字符作为一个叶子结点,各个字符出现的频度作为结点的权值,根据之前介绍的方法构造哈夫曼树

因为哈夫曼树并不唯一,所以哈夫曼编码也不唯一


应用:英文字母频次

在这里插入图片描述

注:不可以传答案!在这里插入图片描述


5.5.2_1 并查集

在这里插入图片描述

漏网之鱼:逻辑结构——“集合”

在这里插入图片描述

补一下没学过的集合,在数学里很常用

在这里插入图片描述
可以将所有元素划分为几个子集:
在这里插入图片描述
那么,在集合的关系之下,任意挑选出两个元素,两个元素间有什么关系呢?

A和H不属于同一个集合
A和E属于同一个集合

回顾:森林

也可以用树、森林这种表示方式来表达出各个元素之间的
在这里插入图片描述

把属于同一个集合的元素组织成一棵树,不同集合的元素放到不同的树里。

用互不相交的树,表示多个“集合”:
在这里插入图片描述

”:指定一个元素,判断到底属于哪一个集合
——从指定元素出发,一路向北,找到根结点,通过根结点判断属于哪个集合
如何判断两个元素是否属于同一个集合?
——分别查到两个元素的根,判断根结点是否相同即可,根结点不一样就是从属于不同的两个集合

在这里插入图片描述

”:把两个合集合并为一个合集
——让一棵树成为另一棵树的子树即可

并查集,本质上是表示一种集合的逻辑关系,对这个集合需要实现的基本操作就是并和查

用双亲表示法。


回忆:树的存储——双亲表示法

在这里插入图片描述

根结点A没有父结点,对应的数组元素是-1;
G这个结点在数组里的位置是6,其父结点是C,C的位置在数组里是2,那么G对应的数据元素就是2指向C的下标。
用一个静态数组描述出各个结点之间的父子关系,方法就是让孩子结点的元素指针指向其父结点所对应的数组下标。
这些指针都是往上指的,给定一个结点要找到结点的父结点,或者一路往上找到其根结点就会很容易。并且想让两棵树并为同一棵树,只需要让一棵树的根结点的parent指向另一棵树的根结点的编号。

使用双亲表示法并和查这两个操作实现起来都会非常方便。

查:找根结点;并:找一棵树的根结点指向另一棵树的根结点


“并查集”的存储结构

在这里插入图片描述
将刚才提到例子中的ABCD……M,分别给他们对应上一个静态的数组元素。

L结点数组下标为11,父结点是E结点数组下标为4,所以和L对应的数组元素S[ ]就是4:在这里插入图片描述
E的父结点是B,B的下标是1,那么E对应值为1:在这里插入图片描述
B的父结点是A,A的下标是0,那么B对应值为0:在这里插入图片描述
A是根,对应数组元素的值是-1

对于其他两棵树也以此类推。

总结并查集:
Q:对于n个数据元素,如何表示这些数据元素之间的集合关系,属于同一集合还是不同集合?
A:声明一个长度为n的int型数组S,用这个数组表示它们的集合关系,本质上就是树的双亲表示法。


“并查集”的基本操作

在这里插入图片描述

查:确定指定元素所属集合
——利用数组一路往上找到根结点
并:合并两个不相交的集合
——让一棵树的根结点所对应的数组的值指向另一棵树的下标

并查集(Disjoint Set)是逻辑结构——集合的一种具体实现,只进行“”和“”两种基本操作


“并查集”的代码实现——初始化

在这里插入图片描述
用一个int型的数组来表示刚开始的这些元素,此时并不知道这些元素到底是否属于同一个集合,可以初始化为各自独立的n个子集,所以初始化并查集时,将所有数组的元素数组的值全部赋值为-1。

表示每个元素之间是各成一派的,每个元素就是独立的一个子集。

初始化之后,就可以根据实际的业务需求来对各个元素,各个集合进行合并,如果两个元素应该划分到同一个子集,那么就把它们并到一起。


“并查集”的代码实现——并、查

在这里插入图片描述

Find操作:找L结点所属集合,用while循环,L是11号,指向4号E结点,指向1号B结点,指向0号A结点,A指向-1,为根结点,那么L就属于A统治的子集。

Union操作:要把绿色和紫色两个集合合并为一个,两个子集的根结点分别为A、C,即传入A和C的数组下标0和2,先判断当前要合并的两个集合是两个不同的集合(相同直接return),然后将一个根结点的数组指针指向另一个根结点,S[2] = 0,这个操作就让C连到A结点下。

Eg. 合并E和G结点:先对它们进行find,找到它们的根结点分别是哪个,然后对根结点进行Union操作合并。


时间复杂度分析

在这里插入图片描述
并:把两个根结点合并,时间复杂度: O ( 1 ) O(1) O(1)

查:查J。左边情况下,查1次(较好的情况);右边情况下,查n次【最坏的情况和树的高度h相关】,时间复杂度 O ( n ) O(n) O(n)

想要优化并查集的效率,那么就要在构造树的时候,让树的高度h尽量低!


Union操作的优化

在这里插入图片描述
是否可以让较小的树合并到较大的树呢?

  1. 如果大树合并到小树:
    在这里插入图片描述

树的高度增加了

  1. 让小树合并到大树:
    在这里插入图片描述

树的高度没有变化

思路:每一次Union都让小树合并到大树上,用根节点的绝对值表示树的结点总数:
在这里插入图片描述

A的值是-6,绝对值为6,代表树有6个结点;
C的值是-2,绝对值为2,代表树有2个结点;
D的值是-5,绝对值为5,代表树有5个结点;
结点总数少的树为小树,让小树的根结点的值指向大树的根结点,并更新大树的结点树,如C合并到A,A的值更新为-8

步骤:
首先判断这两棵树,哪棵更小(由于是负值,负值越大,绝对值越小,结点越少,是小树),C结点更小,要将C结点合并到A结点;
累加结点总数,把-2和-6进行相加;
把更小的树的根结点C指向更大的树的根结点,将S[2] = 0

在这里插入图片描述
再合并另外两棵树:
在这里插入图片描述
可以发现,通过小树合并到大树,树的高度并没有变化。

在这里插入图片描述
对Union进行优化后,树的高度不会超过 [ l o g 2 n ] + 1 [log_2n]+1 [log2n]+1
Find操作的最坏时间复杂度为: O ( l o g 2 n ) O(log_2n) O(log2n)


5.5.2_2 并查集的进一步优化

——补充:并查集的终极优化

拓展:Find 操作的优化(压缩路径)

在这里插入图片描述

要找结点L所属集合,从L出发到E到B到A,这条路径称为查找路径。

压缩路径——Find操作,先找到根节点,再将查找路径上所有结点都挂到根结点下:
在这里插入图片描述
在这里插入图片描述

A为根结点,从L出发可以一次查找这条查找路径上的各个结点,此时需要把查找路径上的结点全都挂到根结点A下面。

在这里插入图片描述
在这里插入图片描述

把L挂到A下

在这里插入图片描述
在这里插入图片描述

把E挂到A下

在这里插入图片描述

此时查找L只需要一次操作,也就是说查找L的路径长度就被压缩了。

给出指定x,找到x所属的根;
向上找到查找路径上的结点,挂到根结点下;

每次Find操作,先找根,再“压缩路径”,可使树的高度不超过𝑂(α(𝑛))。α(𝑛)是一个增长很缓慢的函数,对于常见的n值,通常α(𝑛)≤4,因此优化后并查集的Find、Union操作时间开销都很低

时间复杂度甚至可以 ≤ O ( 4 ) ≤O(4) O(4)

并查集的优化

在这里插入图片描述

Union优化思路:小树合并到大树,时间复杂度不超过 O ( l o g 2 n ) O(log_2n) O(log2n)
Find优化:先找到根节点,再进行“压缩路径”
核心思想:尽可能让树变矮

在这里插入图片描述

合并n个独立元素,需要进行n-1次的Union,每次都需要进行Find操作,所以取决于Find操作的时间复杂度(树高h)。

快乐时刻

请添加图片描述

可以体验模拟并查集算法
https://www.cs.usfca.edu/~galles/visualization/DisjointSets.html

请添加图片描述

以及各种算法
https://www.cs.usfca.edu/~galles/visualization/Algorithms.html


知识回顾与重要考点

5.5.1 哈夫曼树

在这里插入图片描述

  • 树的带权路径长度(WPL)
  • 哈夫曼树
  • 构造哈夫曼树
  • 哈夫曼编发:可用于数据压缩

5.5.2_1 并查集

在这里插入图片描述

  • Union操作时,让小树并入大树
  • 优化后,树高≤ [ l o g 2 n ] + 1 [log_2n]+1 [log2n]+1
    Find -> O ( l o g 2 n ) O(log_2n) O(log2n)

5.5.2_2 并查集的进一步优化

在这里插入图片描述

合并n个独立元素,需要进行n-1次的Union,每次都需要进行Find操作,所以取决于Find操作的时间复杂度(树高h)。


http://www.niftyadmin.cn/n/338843.html

相关文章

【Spring框架】--04.单元测试JUnit、事务、资源操作Resources、国际化、数据校验Validation、提前编译AOT

文章目录 6.单元测试:JUnit6.1整合JUnit56.1.1搭建子模块6.1.2引入依赖6.1.3添加配置文件6.1.4添加java类6.1.5测试 6.2整合JUnit46.2.添加依赖6.2.2测试 7.事务7.1JdbcTemplate7.1.1简介7.1.2准备工作7.1.3实现CURD①装配 JdbcTemplate②测试增删改功能③查询数据返…

基于αβ剪枝算法的五子棋

访问【WRITE-BUG数字空间】_[内附完整源码和文档] 五子棋是世界智力运动会竞技项目之一,是一种两人对弈的纯策略型棋类游戏,是世界智力运动会竞技项目之一,通常双方分别使用黑白两色的棋子,下在棋盘直线与横线的交叉点上&#xf…

敏捷项目管理阶段框架-推测阶段实践

推测阶段实践 关注产品和项目,创造和理解产品待办事项列表和发布计划 怎么做计划、怎么做需求 产品需求规划(怎么做计划) 洋葱圈 愿景、产品路线图、产品发布计划、迭代发布计划、每日计划 滚动式规划,渐进明细,走一…

Edge插件之WeTab,画面优美,可以免费使用chatgpt,很难不爱

目录 一、普通的edge新标签页 二、安装WeTab插件 1.WeTab插件的安装非常简单,只需在百度搜索wetab,进入官网: 2.进入官网,点击edge图标,进入插件下载页面: 3.这里由于我是已经安装成功,显示…

【C++】new和delete

new是个运算符 使用: new 类型(初始值); malloc和new的区别: 1--new申请空间失败抛出异常,malloc返回空指针 ip(new(nothrow) Int(10))//不想它抛出异常 2--new调用构造函数 3--new可以重…

京东商品详情API调用说明 京东商品库存销量接口

尊敬的开发人员: 感谢您选择使用京东API进行开发。下面为您提供一份简要的API调用说明,帮助您快速上手并实现所需功能。 1.注册京东开放平台账户并创建应用 首先,您需要在 https://o0b.cn/jennif/ 网站上注册一个京东开放平台的账户&#…

[Nacos] Nacos Client重要Api (一)

Instance:实例,代表一个Nacos Client主机实例。ServiceInfo:微服务信息实例。其包含着一个Instance列表。NamingService: 该接口只有一个实现类,NacosNamingService。通过这个类的实例,可以完成Client与Ser…

Linux如何部署爬虫

在 Linux 上部署爬虫需要先安装必要的软件和环境,然后编写脚本或选择相应的爬虫框架来完成实际操作。以下是可行的部署过程: 1、安装必要的软件和环境 在 Debian/Ubuntu 系统中使用以下命令安装 Python、pip 和 Git 等软件: sudo apt updat…