平衡二叉树(AVL Tree)

转载至:https://www.cnblogs.com/jielongAI/p/9565776.html

在学习算法的过程中,二叉平衡树是一定会碰到的,这篇博文尽可能简明易懂的介绍下二叉树的相关概念,然后着重讲下什么事平衡二叉树。

(由于作图的时候忽略了箭头的问题,正常的树一般没有箭头,虽然不影响描述的过程,但是还是需要注意,所以还请读者忽略一下部分图的箭头)

一、二叉(查找)树

二叉查找树(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)的更多相关文章

  1. 详解平衡二叉树(AVL tree)平衡操作(图+代码)

    * 左左就右旋,右右就左旋 #include<bits/stdc++.h> using namespace std; typedef long long ll; const int max ...

  2. 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 ...

  3. 平衡二叉树(AVL Tree)

    在学习算法的过程中,二叉平衡树是一定会碰到的,这篇博文尽可能简明易懂的介绍下二叉树的相关概念,然后着重讲下什么事平衡二叉树. (由于作图的时候忽略了箭头的问题,正常的树一般没有箭头,虽然不影响描述的过 ...

  4. 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 ...

  5. 平衡二叉树AVL插入

    平衡二叉树(Balancedbinary tree)是由阿德尔森-维尔斯和兰迪斯(Adelson-Velskiiand Landis)于1962年首先提出的,所以又称为AVL树. 定义:平衡二叉树或为 ...

  6. 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 ...

  7. 数据结构与算法--从平衡二叉树(AVL)到红黑树

    数据结构与算法--从平衡二叉树(AVL)到红黑树 上节学习了二叉查找树.算法的性能取决于树的形状,而树的形状取决于插入键的顺序.在最好的情况下,n个结点的树是完全平衡的,如下图"最好情况&q ...

  8. 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 ...

  9. 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 ...

随机推荐

  1. 把SQLAlchemy查询对象转换成字典

    1-假设查出来的为单个对象 1-1 在model.py中为模型对象添加字典转换函数: from exts import db class User(db.Model): __tablename__ = ...

  2. mybatis和spring的xml基本配置

    mybatis 导入依赖环境 <dependency> <groupId>org.mybatis</groupId> <artifactId>mybat ...

  3. C#TaskScheduler 任务调度器的原理

    什么是TaskScheduler? SynchronizationContext是对"调度程序(scheduler)"的通用抽象.个别框架会有自己的抽象调度程序,比如System. ...

  4. C#foreach 本质( 鸭子类型遍历)

    探讨关于C#中Foreach的本质 要实现foreach需要满足什么条件? 只要类中实现类中的GetEnumerator()方法.MoveNext()方法.Current属性(俗称鸭子类型)都可以使用 ...

  5. 『无为则无心』Python日志 — 67、logging日志模块处理流程

    目录 1.概括理解 2.详细说明 3.应用示例 1.概括理解 了解了四大组件的基本定义之后,我们通过图示的方式来理解下信息的传递过程: 也就是获取的日志信息,进入到Logger日志器中,传递给处理器确 ...

  6. selenium+python安装

    整理了下selenium+python环境搭建,搭建了很多次但每次都还是手忙脚乱,今天用心整理下 selenium 是用于测试 Web 应用程序用户界面 (UI) 的常用框架,并且 Selenium ...

  7. 调用Visual Studio的cl.exe编译C/C++程序

    @ 目录 调用Visual Studio的cl.exe编译C/C++程序 前言 1.查看VS的路径 2.添加环境变量 3.查看设置是否生效 4.配置Notepad++ 调用Visual Studio的 ...

  8. ref属性

    ref属性 1.被用来给元素或组件注册引用信息(id的替代者) 2.应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc) 3.使用方式: 打标识:<h1 ref=& ...

  9. 物理机迁移至vmware

    使用用vmware vcenter converter standalone将物理机迁移到虚拟机. 6.2版本下载地址如下: https://www.filehorse.com/download-vm ...

  10. 闲聊系列之 5-why root cause分析法

    本篇参考: https://max.book118.com/html/2017/1126/141669829.shtm https://baike.baidu.com/item/5why%E5%88% ...