vlambda博客
学习文章列表

数据结构 | 四种平衡二叉树介绍

欢迎观看论文系列

二叉树(Binary Tree)指的是树中结点的度不大于2的有序树,其递归定义是:二叉树是一棵空树,或者是一棵由一个根节点和两棵互不相交的,分别称作根的左子树和右子树组成的非空树;左子树和右子树同样也是二叉树。

二叉树是树形结构的一个重要类型,许多实际问题抽象出来的数据结构往往都是二叉树的类型。即使是一般的树也可以转化为二叉树,并且二叉树的存储结构和算法相对而言都比较简单。因此研究二叉树对于研究数据结构有着重要的意义。

基于二叉树在数据结构中的重要作用,本篇文章中将探讨二叉树的一个重要应用,即作为二叉搜索树时能够发挥的作用。同时,本文也进一步探讨研究了较常使用的平衡二叉树和六种不同的平衡二叉树的性能并对其做了对比分析,为未来计算机应用提供了可供参考的意见。

1  二叉搜索树

1.1  二叉搜索树(BST)

二叉搜索树(Binary Search Tree)又称二叉查找树或二叉排序树。作为一种经典的数据结构,它既有链表的快速插入与删除操作的特点,又有数组快速查找的优势。

二叉搜索树的定义也是递归的。它是一棵空树,或者是满足下列性质的二叉树:

  1. 每个结点都有一个作为搜索依据的关键码(key),所有节点的关键码互不相同;

  2. 左子树(如果存在)上所有结点的关键码都小于根;

  3. 右子树(如果存在)上所有结点的关键码都大于根;

  4. 左子树和右子树也是一棵二叉搜索树。

如下图所示,就是一棵标准的二叉搜索树:


1.1.1 搜索

二叉树的搜索是一个递归的过程,流程图如下所示:


数据结构 | 四种平衡二叉树介绍

从树根出发,逐步的缩小查找范围,直到发现目标(成功)或者缩小至空结点(失败)。对于该过程而言,在二叉搜索树的每一层,查找算法至多访问一个结点,并且只需要常数时间。因此,总体所需时间应当线性正比于查找路径的长度,或最终返回结点的深度。对于规模为n的二叉搜索树,深度在最坏情况下可达(n)。

1.1.2 插入

二叉搜索树的插入的流程图如下所示:


数据结构 | 四种平衡二叉树介绍

为了在二叉搜索树中插入一个结点,首先需要利用其搜索操作查找到插入的位置,然后再将新结点作为叶子结点插入(或者更新已存在结点的值)。由于该操作依赖于搜索操作,因此时间复杂度也为O(n)。

1.1.3 删除

二叉搜索树的删除的流程图如下所示:


数据结构 | 四种平衡二叉树介绍

二叉搜索树的删除过程也依赖于其查找过程,故时间复杂度也为O(n)。

1.2  平衡二叉搜索树(BBST)

由于二叉搜索树在最坏情况下可能退化为列表,此时的各种效率降至O(n)。因此如果不能有效的控制树高,二叉搜索树无法体现出明显的优势。因此,需要依照某种宽松的标准,重新定义二叉搜索树的平衡性。

平衡二叉搜索树(Balanced Binary Search Tree)指的是在某种约束条件下,树高渐进地不超过O(logN)。在这一约束条件下,各种操作的时间复杂度即可降低为O(logN)。它为每一个结点v的平衡因子(balance factor)定义为左、右子树的高度差,即

balFac(v) = height(lc(v)) - height(rc(v))

下图即为定义了平衡因子的二叉搜索树:


数据结构 | 四种平衡二叉树介绍

通过设定这种约束条件,即可达到搜索树的最佳性能。后续将介绍六种二叉搜索树。

2 六种平衡二叉搜索树

2.1 高度平衡树(AVL)

AVL是最早发明自平衡二叉树,它被称作高度平衡树。在ALV树中,任意结点平衡因子的绝对值不超过1,即:

|balFac(v)|≤1

2.1.1 平衡性


数据结构 | 四种平衡二叉树介绍

如上图所示,不妨设结点数最少的AVL树S的根结点为r,r的左、右子树分别为lc和rc,记规模为|lc|和|rc|,高度为h_l和h_r,则有:|S|=1+|lc|+|rc|由于S的子树lc和rc也是AVL树,并且高度不超过h-1。因此,不妨取:h_l=h-1, h_r=h-2,|S|=1+| S(h-1) |+| S(h-2) |,由归纳假设,可以获得如下关系:|S|=1+(fib(h+2)-1)+(fib(h+1)-1)而根据Fibonacci数列的定义,可以获得:|S|=fib(h+2)+fib(h+1)-1=fib(h+3)-1             因此,可以知道:高度为h的AVL树至少包含fib(h+3)-1个结点。由于fib(h)和n次方正比,于是包含n个结点的AVL树的高度应为O(log⁡n )。综上可知,AVL树的确是平衡的。

2.1.2 旋转

AVL树的插入过程和一般二叉搜索树的过程相同,即:从树根出发,逐步的缩小查找范围,直到发现目标(成功)或者缩小至空结点(失败)。

不同之处在于,二叉树在插入或者删除时,需要维持其平衡。而维持平衡这一过程,正是通过旋转(Rotate)操作来实现的。AVL树的旋转可以归纳为四种:LL单右旋、RR单左旋、LR先左旋再右旋和RL先右旋再左旋。

  • LL单右旋:当失衡位置发生在失衡结点左子树的左子树时,需要进行LL单右旋。


数据结构 | 四种平衡二叉树介绍

  • RR单左旋:当失衡位置发生在失衡结点右子树的右子树时,需要进行RR单左旋。


数据结构 | 四种平衡二叉树介绍

  • LR先左旋再右旋:当失衡位置发生在失衡结点左子树的右子树时,需要进行LR先左旋再右旋。


数据结构 | 四种平衡二叉树介绍

  • RL先右旋再左旋:当失衡位置发生在失衡结点右子树的左子树时,需要进行RL先右旋再左旋。


数据结构 | 四种平衡二叉树介绍

2.1.3 性能分析

通过2.1.2的旋转操作,AVL树可以满足其性质的定义,通过2.1.1证明可知:包含n个结点的AVL树的高度应为logn 。

由于二叉搜索树的查找操作时间正比于树高,因此AVL树查找的时间复杂度为O(logN) 。同理,插入和删除的时间复杂度也为(logN)。

2.2 伸展树(ST)

伸展树(Splay Tree)也叫分裂树,相对于AVL树实现更为简捷。伸展树无需时刻都严格地保持全树的平衡,但却能够在任何足够长的真实操作序列中,保持分摊意义上的高效率。伸展树也不需要对基本的二叉树节点结构,做任何附加的要求或改动,更不需要记录平衡因子或高度之类的额外信息,故适用范围更广。

每次查找节点之后对树进行重构,把被查找的节点搬移到树根。每次对伸展树进行操作后,它均会通过伸展(Splay)的方法使得被访问结点置于根结点的位置。

2.2.1 伸展

与普通的二叉搜索树的区别在于,伸展树通过伸展操作将元素调整至根结点的位置。这一调整根据结点所在位置的不同,采用不同的调整方法:

情形 旋转方法
单R型 左旋转
单L型 右旋转
RR型 两次左旋转
LL型 两次右旋转
RL型 先右旋转再左旋转
LR型 先左旋转再右旋转

2.2.2 性能分析

伸展树能够在O(log⁡n)内完成插入、查找和删除操作。它在不保证最坏情况下的时间复杂度是O(log⁡n),其边界是均摊的。但是,它有一个最显著的缺点,即有可能变成一条链,这种情况下时间复杂度高达O(n)。

2.3 红黑树(RBT)

红黑树(Red Black Tree)是一种自平衡二叉搜索树,它等价于4阶B-树。它实际上是一种特化的AVL,均是在进行插入和删除操作时通过特定操作保持二叉搜索树的平衡,从而获得了较高的查找性能。

红黑树保持适度平衡的关键在于:任一结点左、右子树的高度,相差不得超过两倍。为了保持这一平衡,红黑树为其内部的每一个结点定义了颜色属性黑色或者红色(本文章中红黑树黑底表示黑色结点,白底表示红色结点)。


数据结构 | 四种平衡二叉树介绍

与上述二叉搜索树不同,红黑树引入了外部结点作为叶结点(图11中的黑色矩形即为叶结点),它满足下述性质:

  1. 每个结点要么是黑色,要么是红色;

  2. 根结点是黑色的;

  3. 每个叶结点(NIL)是黑色的;

  4. 每个红色结点的两个子结点一定都是黑色的;

  5. 任意一个结点到每个叶结点的路径都包含相同数量的黑色结点。

事实上,正是由于性质(5),红黑树的这种平衡被称作黑色完美平衡。红黑树维持平衡主要依靠三种方法:左旋、右旋和染色(结点的颜色由黑变红或者由红变黑)。

2.3.1 平衡性

2.3.2 三种操作

由于红黑树是一棵二叉搜索树,并且查找并不会破坏树的平衡,因此查找和二叉搜索树的查找无异,是一个递归的过程。

红黑树的插入过程分为两步,第一步是查找到插入位置,第二步则是插入后的自平衡。因此,其插入过程可分为下述情况(如图11所示):

  1. 红黑树为空树:将插入的结点作为根结点,并且染色为黑色;

  2. 插入结点的父结点为黑色结点:直接插入即可;

  3. 插入结点的父结点为红色结点:

    若叔叔结点存在并且为红色结点,则只需将P和S设置为黑色,PP设置为红色,接下来递归的处理PP结点即可;

    数据结构 | 四种平衡二叉树介绍


    若叔叔结点不存在或者为黑色结点,插入结点是父结点的左子结点,父结点是祖先结点的左子结点。该种情况需要将P设为黑色,PP设为红色,同时对PP进行右旋,如下图所示。其余情况实际为该种情况的对称形式,在此不再赘述。


    数据结构 | 四种平衡二叉树介绍

红黑树的删除过程也是先查找到删除位置,然后通过旋转和染色来实现再次平衡。

2.4 替罪羊树(ST)

替罪羊树(Scapegoat Tree)不同于上述通过旋转来维持相对平衡的二叉搜索树,它是基于一种暴力重构的操作来保持相对平衡的。

2.4.1 平衡因子

替罪羊树中定义了一个平衡因子α的概念,范围在0.5到1之间。当某个结点的总结点数* α <它某个子树的总结点数,便会对该结点执行重构操作。对于α的取值,如果α越小,那么对平衡的要求越高,重构的次数会更多;α越大,树的平衡程度越低,重构的次数也会随之减少。一般而言,α取0.7左右比较合适。

2.4.2 平衡性

2.4.3 重构


数据结构 | 四种平衡二叉树介绍

如上图所示的替罪羊树,若平衡因子  ,则结点v已发生失衡。重构过程如下:

  1. 将这棵树压扁,存入向量中:


2.重新建树:每次取区间中点作为根结点,递归左右两边子树建树:


通过上述操作,替罪羊树即完成了其重构操作。并且由2.4.2中结论可知,每次插入后的重构仍然能保证替罪羊树各种操作的时间复杂度达到  。

3 平衡搜索树的性能对比

根据上述介绍可以看出,AVL、伸展树、红黑树和替罪羊树分别采取了不同的方法实现了相对平衡,复杂度如表2所示。在实际使用时,应当根据工作情景选用:

平衡树 时间复杂度 应用
AVL O(log n) 最早的平衡树
伸展树 均摊O(log n) 对区间操作
红黑树 O(log n) 综合效率最高
替罪羊树 O(log n) 实现较易

编辑:汪汪汪

来源:各种csdn文章