转载:平衡二叉树(AVL Tree)
平衡二叉树(AVL Tree)
在学习算法的过程中,二叉平衡树是一定会碰到的,这篇博文尽可能简明易懂的介绍下二叉树的相关概念,然后着重讲下什么事平衡二叉树。
(由于作图的时候忽略了箭头的问题,正常的树一般没有箭头,虽然不影响描述的过程,但是还是需要注意,所以还请读者忽略一下部分图的箭头)
一、二叉(查找)树
二叉查找树(Binary Search Tree)是二叉树的一种,其树节点(internal nodes of the tree)储存着键值并且满足以下特性并如图A所示:
- 假设u, v, r分别为树的三个结点(nodes),r为树的根节点,u为根的左子树,v为根结点的右子树;
- 键值大小关系:key(u) < key(r) < key(v),也就是位于根结点(亦或是父节点)的左子树的所有结点的值都是小于根或者父结点的,而位于右子树的结点都大于根或者父结点;
- 树的外部结点不储存任何的信息。
图A 二叉查找树
二、二叉查找树的操作
2.1 查找(Search)
如若要查找二叉树中的某个元素k,我们会从根节点朝着树结构往下寻找对应的结点,所寻找的结点方向取决于当前结点与所要寻找的结点的值的对比。基于图A,假设我们现在所要寻找的结点是7,那么从根结点开始,我们可以知道7 < 8,那么往下朝着结点值为6的子树走,然后我们发现6 < 7所以此时我们就寻找结点为6的右子树,这时我们发现7 = 7,也就是到达了我们所要寻找的结点了,下图B是寻找结点的过程:
图B 二叉树查找过程
算法伪代码:
1
2
3
4
5
6
7
8
9
|
BSTreeSearch(k, v): if T.isExternal(v): return v elif k < key(v): return BSTreeSearch(k, T.left(v)) elif k = = key(v): return v else k > key(v): return BSTreeSearch(k, T.right(v)) |
2.2 插入(Insertion)
如果要执行插入操作,我们首先需要对树进行查找操作,找到相应的节点的叶子(leaf)结点,然后再执行插入操作。下面以插入5位例子进行执行,如果要插入5,执行2.1节中所说的二叉树的查找操作,我们可以发现5 < 8,5 < 6,5 > 4,这时我们可以发现最终5是大于4的,那么我们需要在4的右叶子结点插入5,并且延伸新插入的结点(5)的叶子结点。具体操作如下图C所示:
图C 二叉树插入操作
2.3 删除(Deletion)
相应的执行删除操作,我们也先需要查找到相对应的结点,然后将该结点从二叉树中移除(remove),实际代码实现中需要判断要删除的结点的键值是否存在于二叉树中,除此之外,如果该结点伴有叶子结点,那么需要将该结点和叶子结点一同移除。当然,这里亦有另外一种情况,就是被移除的结点的左右子树都是内部结点(internal nodes),这个时候的操作会稍微复杂一点,这里以删除4和6为例子来讲述这两种情况,具体的操作如图D(基于图C进行操作)所示:
![]() |
![]() |
图D 删除操作
删除结点4的操作相对简单,只需要移除该结点和相应的叶子节点即可,但是相反的删除6的时候,我们需要确保所有父结点的左子树都是小于该结点的,并且右子树都是大于该父结点的,所以当我们删除6的时候,我们需要将5移到相应的位置并在相应的叶子结点补上新的叶子。
2.4 算法表现(Performance)
假设一个二叉树的高度为H,最坏的操作情况是O(n),而最好执行情况则为O(log(n))。
三、二叉平衡树(AVL TREE)
3.1 平衡二叉树的定义
二叉平衡树指的是要么它本身是一个空树,要么它是一个左子树和右子树的深度之差的绝对值不大于1,并且保证左右子树都是平衡树,图E是一个平衡二叉树。从图中我们可以看出,一个结点的高度位1则表明为其叶子结点到父结点的高度,整颗树的高度取决于最深叶子结点到根结点的距离。
图E 平衡二叉树
3.2 平衡二叉树的操作
AVL树的查找操作和普通的二叉树的查找基本一致,但是插入和删除操作有所不同,因为插入和删除会减少树的结点并且改变树的结构,这个时候为了使树始终保持平衡状态我们需要对树进行重构使其始终保持平衡状态,一般这个操作叫做旋转操作(rotation),旋转分为左旋转和右旋转等,下面就具体来看看插入和删除操作及如何运用旋转使二叉平衡树在插入和删除某结点之后依然保持平衡。
3.2.1 旋转操作
在这个小节中主要介绍一下左旋转和右旋转,旋转操作不局限于这两个,但是基本原理都一样,最终目的就是为了让二叉平衡树在被操作之后再次达到平衡。
图F 左旋转
在上图所说的左旋转操作中,我们假设的是x < y < z,因为树不平衡了,我们执行左旋转,将x及其左子树进行左旋转,并且将原本y的左子树变为x的右子树,这里需要注意的两点,①就是我们需要寻找到三个点,这三个点的大小是有排序的,如这此段开头所说道的xyz的关系,将中间那个值作为新的中心结点,然后再进行旋转操作,②就是一定要确保所有的左右子树遵循二叉树的定义要求,既左子树一定要永远都是小于其父结点的,而右子树始终大于父结点的。
图G 右旋转
图G所述的三个节点的关系为z < x < y,因此根据左旋转所描述的我们可以知道x应该作为中心结点也就是父结点,然后这里需要进行两次旋转才能使二叉树最终处于平衡,首先是先对z进行左旋转,将z变为x的左子树,然后再对y进行右旋转,在这个过程中,x的左子树变为z的右子树,而右子树则成为了y的左子树。有了基本的这两个操作,接下来我们就根据实际例子来看看对平衡二叉树执行插入和删除的操作并且结合旋转来达到平衡状态。
3.2.2 插入操作
平衡二叉树的插入操作与普通二叉查找树的操作一样,新插入的节点都发生在叶子结点,唯一不同的就如上述所说,新插入的结点致使树的结构发生改变而导致不平衡,此时需要进行旋转以达到平衡。在图E的基础上插入一个新结点,结点的值为40,新得到的图如图H所示:
图H 插入新的节点Key(40)
这时我们会发现此时的二叉树已经不平衡,这时我们需要寻找到树里面导致树不平衡的三个点,进行相应的操作,具体有以下两步:① 先对结点39以结点42为父结点进行左旋转,此时节点40变成了39的右结点,而33,39,40一起成为了结点42的左子树。② 对结点53进行右旋转,将其变成节点42的右子树,结点55依然为结点53的右子树。由此便完成了整棵树的重构并让新的树保持平衡。重构之后的树如下图I所示:
图I 重构之后得到的树
3.2.3 删除操作
假设在图I的基础上删除结点22,那么此时我们从节点19开始便利找到第一个导致不平衡的结点为25并且具有最大高度值的结点,之后往右子树进行便利寻找到第二个具有最大高度值的结点,此结点为42(下图标注了红色边框的结点),
图J 删除之后进行重构流程图
3.2.4 二叉平衡树的算法表现
二叉平衡树的算法表现主要体现在以下几个方面:
- 如果单独重构一次所需要的运行时间为O(1)
- 如果查找二叉平衡树中某个结点的话为O(log(n))
- 插入操作为O(log(n)),若需要重构,为了保持平衡所需要的时间为O(log(n))
- 删除操作为O(log(n)),若需要重构,为了保持平衡所需要的时间为O(log(n))
以上便是有关二叉树和二叉平衡树相关的知识点,如果有哪里讲的不对的,还请读者指出,谢谢!
转载:平衡二叉树(AVL Tree)的更多相关文章
- 详解平衡二叉树(AVL tree)平衡操作(图+代码)
* 左左就右旋,右右就左旋 #include<bits/stdc++.h> using namespace std; typedef long long ll; const int max ...
- PAT A1123 Is It a Complete AVL Tree (30 分)——AVL平衡二叉树,完全二叉树
An AVL tree is a self-balancing binary search tree. In an AVL tree, the heights of the two child sub ...
- 平衡二叉树(AVL Tree)
在学习算法的过程中,二叉平衡树是一定会碰到的,这篇博文尽可能简明易懂的介绍下二叉树的相关概念,然后着重讲下什么事平衡二叉树. (由于作图的时候忽略了箭头的问题,正常的树一般没有箭头,虽然不影响描述的过 ...
- PAT 甲级 1066 Root of AVL Tree (25 分)(快速掌握平衡二叉树的旋转,内含代码和注解)***
1066 Root of AVL Tree (25 分) An AVL tree is a self-balancing binary search tree. In an AVL tree, t ...
- 平衡二叉树AVL插入
平衡二叉树(Balancedbinary tree)是由阿德尔森-维尔斯和兰迪斯(Adelson-Velskiiand Landis)于1962年首先提出的,所以又称为AVL树. 定义:平衡二叉树或为 ...
- 04-树5 Root of AVL Tree
平衡二叉树 LL RR LR RL 注意画图理解法 An AVL tree is a self-balancing binary search tree. In an AVL tree, the he ...
- 数据结构与算法--从平衡二叉树(AVL)到红黑树
数据结构与算法--从平衡二叉树(AVL)到红黑树 上节学习了二叉查找树.算法的性能取决于树的形状,而树的形状取决于插入键的顺序.在最好的情况下,n个结点的树是完全平衡的,如下图"最好情况&q ...
- A1123. Is It a Complete AVL Tree
An AVL tree is a self-balancing binary search tree. In an AVL tree, the heights of the two child sub ...
- 04-树5 Root of AVL Tree + AVL树操作集
平衡二叉树-课程视频 An AVL tree is a self-balancing binary search tree. In an AVL tree, the heights of the tw ...
随机推荐
- JAVA只要掌握内部类,多继承和单继承都不是问题
摘要:如果实现java的多继承,其实很简单,关键是对于内部类的特征的掌握,内部类可以继承一个与外部类无关的类,保证了内部类天然独立性,根据这个特性从而实现一个类可以继承多个类的效果. 本文分享自华为云 ...
- Qt:QNetworkReply
0.说明 QNetworkReply对象包含了Manager发送的请求头和返回的数据. 它继承自QIODevice,所以可以用各种read获取其中返回的数据: QByteArray data = re ...
- ibv_close_device()函数
int ibv_close_device(struct ibv_context *context); 描述 函数用来关闭一个RDMA设备context: 注意: 函数不能用来释放与该Context关联 ...
- Go基础知识梳理(三)
Go基础知识梳理(三) 结构 type Person struct { name string sex int } func main() { //推荐写法 person := Person{ nam ...
- JVM内存模型及GC机制
一.JVM简介 1.1什么是JVM JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各 ...
- 七天接手react项目-起步
七天接手react项目-起步 背景 假如七天后必须接手一个 react 项目(spug - 一个开源运维平台),而笔者只会 vue,之前没有接触过 react,此刻能做的就是立刻展开一个"7 ...
- 微信小程序断网处理
wx.onNetworkStatusChange(function callback) 参数 function callback 网络状态变化事件的回调函数 参数 Object res 属性 类型 说 ...
- OSPF协议原理及配置4-邻接关系的建立和LSDB同步
OSPF协议原理及配置4-邻接关系的建立和LSDB同步 进入ExStart状态后,广播和NBMA型网络要等待4倍的Hello时间,确定DR和BDR.然后建立邻接关系,并交互链路状态通告,以使用LS ...
- Python时间处理,datetime中的strftime/strptime+pandas.DataFrame.pivot_table(像groupby之类 的操作)
python中datetime模块非常好用,提供了日期格式和字符串格式相互转化的函数strftime/strptime 1.由日期格式转化为字符串格式的函数为: datetime.datetime.s ...
- Android 12(S) 图形显示系统 - BufferQueue的工作流程(十)
题外话 疫情隔离在家,周末还在努力学习的我 ..... 一.前言 上一篇文章中,有基本讲清楚Producer一端的处理逻辑,最后也留下了一个疑问: Consumer是什么时候来消费数据的?他是自己主 ...