红黑树原理分析(图解)

一.为何要有红黑树这种数据结构?html


   学过二叉查找树的同窗都知道,普通的二叉查找树在极端状况下可退化成链表,此时的增删查O(n)效率都会比较低下。为了不这种状况,就出现了一些自平衡的查找树,好比 AVL。segmentfault

 ALV树是一种严格按照定义来实现的平衡二叉查找树,因此它查找的效率很是稳定,为O(log n),因为其严格按照左右子树高度差不大于1的规则,插入和删除操做中须要大量且复杂的操做来保持ALV树的平衡(左旋和右旋),所以ALV树适用于大量查询,少许插入和删除的场景中。数据结构

  如今假设有这样一种场景:大量查询,插入和删除,使用ALV树就不太合适了,由于ALV树大量的插入和删除会很是耗时间,那么咱们是否能够下降ALV树对平衡性的要求从而达到快速的插入和删除呢?性能

  答案确定是有的,红黑树这种数据结构就应运而生了(由于ALV树是高度平衡的,因此查找起来确定比红黑树快,可是红黑树在插入和删除方面的性能就远远不是ALV树所能比的了)。ui

 

二.红黑树的性质spa


 红黑树经过以下的性质定义实现自平衡:.net

1.节点是红色或黑色。
2.根是黑色。
3.全部叶子都是黑色(叶子是NIL节点)。
4.每一个红色节点必须有两个黑色的子节点。(从每一个叶子到根的全部路径上不能有两个连续的红色节点。)
5.从任一节点到其每一个叶子的全部简单路径都包含相同数目的黑色节点(简称黑高)。

 有了上面的几个性质做为限制,便可避免二叉查找树退化成单链表的状况。可是,仅仅避免这种状况还不够,这里还要考虑某个节点到其每一个叶子节点路径长度的问题。若是某些路径长度过长,那么,在对这些路径上的及诶单进行增删查操做时,效率也会大大下降。这个时候性质4和性质5用途就凸显了,有了这两个性质做为约束,便可保证任意节点到其每一个叶子节点路径最长不会超过最短路径的2倍。3d

 当某条路径最短时,这条路径必然都是由黑色节点构成。当某条路径长度最长时,这条路径必然是由红色和黑色节点相间构成(性质4限定了不能出现两个连续的红色节点)。而性质5又限定了从任一节点到其每一个叶子节点的全部路径必须包含相同数量的黑色节点。此时,在路径最长的状况下,路径上红色节点数量 = 黑色节点数量。该路径长度为两倍黑色节点数量,也就是最短路径长度的2倍。举例说明一下,请看下图:code

 

三.红黑树的基本操做之旋转htm


 旋转操做分为左旋和右旋,左旋是将某个节点旋转为其右孩子的左孩子,而右旋是节点旋转为其左孩子的右孩子。这话听起来有点绕,因此仍是请看下图:

上图包含了左旋和右旋的示意图,这里以右旋为例进行说明,右旋节点 M 的步骤以下:

  1. 将节点 M 的左孩子引用指向节点 E 的右孩子
  2. 将节点 E 的右孩子引用指向节点 M,完成旋转

 

四.红黑树的基本操做之添加元素


  红黑树的插入过程和二叉查找树插入过程基本相似,不一样的地方在于,红黑树插入新节点后,须要进行调整,以知足红黑树的性质。性质1规定红黑树节点的颜色要么是红色要么是黑色,那么在插入新节点时,这个节点应该是红色仍是黑色呢?答案是红色,缘由也不难理解。若是插入的节点是黑色,那么这个节点所在路径比其余路径多出一个黑色节点,这个调整起来会比较麻烦(参考红黑树的删除操做,就知道为啥多一个或少一个黑色节点时,调整起来这么麻烦了)。若是插入的节点是红色,此时全部路径上的黑色节点数量不变,仅可能会出现两个连续的红色节点的状况。这种状况下,经过变色和旋转进行调整便可,比以前的简单多了。

 如今咱们来分析一下新增的节点(红色)插入以后可能面临的几种状况,以及他们的处理措施:

1.插入的节点为根节点

 新插入的红色节点变成黑色节点,知足根节点为黑色节点的要求

2.父亲节点为黑色节点

 这个时候不须要进行任何调整操做,此时的树仍然是一颗标准的红黑树

3.父亲节点为红色节点的状况下,叔叔节点为红色节点(不用考虑左右)

 解决方案:将叔叔和父亲节点改成黑色,爷爷节点改成红色,而后又将爷爷节点看成插入节点看待,一直进行上面的操做,直到当前节点为根节点,而后将根节点变成黑色

 

4.父亲节点为红色,叔叔节点为黑色

1)父亲节点为爷爷节点的左孩子,新插入节点为父节点的左孩子(左左)

 解决方案:将父亲节点和爷爷节点颜色互换(父节点变为黑色,爷爷节点变为红色),而后对爷爷节点进行一次右旋

 

 注:上图叔叔是空叶子节点,因此也是黑色

2)父亲节点为爷爷节点的右孩子,新插入节点为父节点的右孩子(右右)

 解决方案:将父亲节点和爷爷节点颜色互换(父节点变为黑色,爷爷节点变为红色),而后对爷爷节点进行一次左旋

3)父亲节点为爷爷节点的左孩子,新插入节点为父节点的右孩子(左右)

 解决方案:对父亲节点进行一次左旋,而后就变成了状况1,按照状况1再进行处理

4)父亲节点为爷爷节点的右孩子,新插入节点为父节点的左孩子(右左)

 解决方案:对父亲节点进行一次右旋,而后就变成了状况2,按照状况2再进行处理

 

 

五.红黑树的基本操做之删除元素


  相较于插入操做,红黑树的删除操做则要更为复杂一些。删除操做首先要肯定待删除节点有几个孩子,若是有两个孩子,不能直接删除该节点。而是要先找到该节点的前驱(该节点左子树中最大的节点)或者后继(该节点右子树中最小的节点),而后将前驱或者后继的值复制到要删除的节点中,最后再将前驱或后继删除。因为前驱和后继至多只有一个孩子节点,这样咱们就把原来要删除的节点有两个孩子的问题转化为只有一个孩子节点的问题,问题被简化了一些。

删除一个节点有如下四种状况:

 1.删除的节点没有孩子

 2.删除的节点只有左子树

   3.删除的节点只有右子树

   *4.删除的节点拥有左子树和右子树

 其实只有上面前三种状况,对于第四种状况,能够找到待删除节点的直接后继节点,用这个节点的值替代待删除节点,接着状况转变为删除这个直接后继节点,状况也变为前三种之一。

 

1.删除的节点只有左子树或只有右子树

       或者   

 

 只有上面两种状况会存在于红黑树中,直接用DL/DR的元素值代替D的元素,再把DL/DR直接删去就好。

2.删除的节点没有孩子

1)待删除节点是红色的,直接删去这个节点。

2)父节点P是红色节点

 解决方案:把P节点染成黑色,兄弟节点染成红色,删除节点D。

 

3)兄弟节点S是红色节点

  或者  

 解决方案:把P染成红色,S染成黑色,而后以P为轴作相应的旋转操做(D为P的左子树则左旋,不然右旋),变成了状况2(父节点为红色),按照状况2进行操做。

 

4)节点D的远亲侄子为红色节点的状况(父节点P可红可黑)

 解决方案:交换P和S的颜色,而后把远亲侄子节点SR/SL设置为黑色,再已P为轴作相应的旋转操做(D为P的左子树则左旋,不然右旋),删除节点D。

5)节点D的近亲侄子为红色节点的状况(父节点P可红可黑)

 解决方案:把S染成红色,把近亲侄子节点SR/SL染成黑色,而后以节点S为轴作相应的旋转操做(D为P的左子树则右旋,不然左旋),变成了状况4,按照状况4进行操做。

6)节点D,P,S均为黑色节点

 解决方案:把D删去,而后把节点S染成红色。

  ①从节点P往上依然是全黑的状况

  ②从节点P往上是其余状况

 

 

参考博客1:https://segmentfault.com/a/1190000012728513

参考博客2:https://www.cnblogs.com/yinbiao/p/10732600.html

参考博客3:https://www.cnblogs.com/GNLin0820/p/9668378.html

参考博客4:https://blog.csdn.net/tanrui519521/article/details/80980135

相关文章
相关标签/搜索