数据结构——平衡查找树与红黑树(1)

今天来总结一下红黑树,因为红黑树的复杂性,打算分为三四次总结完成,话不多说,开始。


红黑树是一种特殊的二叉树,相比一般的二叉查找树,有着诸多的有点,在总结红黑树之前,我们先来谈谈AVL即平衡查找树。在理想情况下,我们的二叉查找树能够保证所有的查找都在lgN次比较中结束,但是当我们按照顺序输入时,将达到最坏的情况,二叉查找树将变成类似链表的一种结构,在这种情况下,将丧失其速度优势。

当我们按照升序插入时,就会发生这种情况,除此之外,对其中一部分进行顺序插入也会导致查找树某一子树深度过深而导致性能的下降。如果我们能够在每一次插入时都保证同一节点的左右子树高度相同或相差极小,我们就能够保证一棵二叉树的平衡,从而降低深度,提升查找效率。


网上已经有太多关于平衡二叉树的文章,通过对于节点的左旋,右旋的方式来调整树的高度从而达到平衡。当时,在动态插入中,要保证树的完美平衡的代价太高。在这里总结一下另一种平衡二叉树。

2-3查找树

这种查找树中允许一个节点保存多个键,我们将标准二叉查找树中的节点称之为2-结点(每个节点含有1个键和两条链接),而这种新结点称为3-结点(每个结点含有2个键和3个链接)

图有点粗糙,画图工具画的,请。。谅解

2-结点很容易理解,就是我们之前使用的二叉查找树中维护的Node类,左侧数据小,右侧数据大。而3-结点同理,只不过将AB分为了三个区间,即大于B,小于A,在AB之间,所以其子节点所对应的数据即为D小于A,E在AB之间,F大于B。

一棵完美的平衡查找树中所有链接到达根节点的距离都应该是相同的。下面我们来看2-3树各项操作应该如何实现。


查找操作

2-3树的查找操作实际上就是二叉查找树的一般化,我们只需要将情况分为两种。

  • 当我们遇到3-结点时,数据需要进行三次比较,并按照比较结果继续向下查找
  • 当我们遇到2-结点时,数据进行两次比较,二分查找。

该图为《算法》3.3中的插图,对于2-3树查找有着非常好的描述。当我们需要查找元素H时,从根M开始比较,因为M是一个2-结点,所以我们比较两次,然后查找到M的左子树3-节点,H位于E和J中间区间,向中间查找,命中H。

这个查找过程非常容易理解同时易于实现,我们只需要将二叉查找树的代码中添加对于3-结点的查找操作并用if进行判断即可,今天不进行任何代码的实现步骤。


插入

插入操作实际上时2-3树中最为核心的部分,如果按照二叉查找树中的思路,向2-结点和3-结点中插入数据实际上对于树的平衡度并没有任何帮助,而且还需要在其内部维护两种结点类,这是非常浪费资源的。

而2-3树中的插入操作能够使两种结点相互转化,从而提升或者降低树的高度,同时保证树的平衡性。

  • 向2-结点中插入新键

其实我们可以向二叉查找树一样进行一次对树的遍历比较,然后将新键挂在某个子树的左右子树中。但是这样就失去了意义,这样的操作一样无法保证树的平衡性,我们可能在某些连续按顺序插入操作后,一棵树就无法保证性能。所有,我们在这里对2-结点插入操作是将该2-结点转化为一个3-节点,深度不发生变化,将键保存如转化后的3-结点即可。如图。

  • 向3-节点中插入新键

与之前情况相比,这种情况将要复杂一些,在这种情况下,我们无法再进行节点的转换,势必会怎加树的高度,但是我们需要通过一种方式让树深度进行均匀的分布,不会出现某一子树过于深的情况。

在考虑一般情况前,先简单考虑向3-结点中如何插入一个新键。为了将新键插入,我们先将键临时放入该结点中,此时,该节点就变成了一个4-结点(拥有3个键和四个链接),创建该节点的原因在于我们可以非常方便的将其转化为一棵有3个2-节点组成的子树,通过这种操作,我们可以将其转化为完美平衡的一棵二叉树,插入前树高度为0,插入后高度为1,同时保证空链接到根节点的距离相同。

下面让我们结合父节点和子节点的情况来考虑更加复杂的插入问题。

向一个父节点为2-结点的3-结点中插入新键。

其插入方式实际上是以上两种的结合,我们先对3-结点进行操作,通过插入转化后,我们将获得一个有3个2-节点组成的完美平衡的子树,而该子树的根将于2-节点相连,此时我们应该想办法将新树的高度降低,非常简单的办法便是将该根部与2-结点合并组成一个3-节点,此时,树的高度将于原来相同,平衡得以保证。

通过这种变化后的树仍是有序的,我们可以发现由于3-节点的区间性,当二者合并时,其排序仍然为左小右大的顺序。

向一个父节点为3-节点的3-节点中插入新键。

这将是我们目前所遇到最麻烦的问题,我们知道,无论我们如何变化,这两个节点都是无法再插入新键并长久保持(4-节点只是暂时转化使用,并不进行长久存储),所有我们需要对其父节点的更上层进行调整来完成这一插入。

向当前3-节点插入后,成为一个4节点并转化为一棵平衡二叉树,此时的根节点为一个2-节点,而其父节点为一个3-节点,那么我们继续将根节点和其父节点合并并转化,将会的到一棵更大的二叉平衡树,此时,我们需要考虑该父节点的父节点,如果是2-节点,我们只需要将两个节点进行合并即可,如果是3-节点,这种情况将和前面一样,我们继续合并转换,并再向树的上端检查。

所有,实际上我们在这种插入开始合并后将遇到两种情况,父节点的父节点为2-节点,父节点的父节点为3-节点,这种情况非常适合递归处理。

假如我们直到检查到根节点仍然为一个3-节点时,我们应该如何处理。这时,我们需要将根节点与其子节点合并为4-节点后转化为2-节点即可,此时树的深度将增加1.

 

 

 

 

 

 

 

 

 

 

 

 

左图为插入操作,右图为分割根节点操作,结合图非常容易理解。


下面用《算法》中的图来总结一下局部变换。

一共为6种情况

本文原创,转载请标明。

猜你喜欢

转载自blog.csdn.net/weixin_41582192/article/details/81608618