【转】红黑树

大部分博客内容转载自https://zhuanlan.zhihu.com/p/24367771

红黑树的特性

红黑树是一种自平衡的二叉查找树,具有二叉查找树的特性外,还具有下面特性

  1. 每个节点不是红色就是黑色的;
  2. 根节点总是黑色的;
  3. 所有的叶节点都是是黑色的(红黑树的叶子节点都是空节点(NIL或者NULL));
  4. 如果节点是红色的,则它的子节点必须是黑色的(反之不一定);
  5. 从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)。

路径: 从一个结点到另一个结点,经过的线段条数,也就是结点数量减1
黑深度——从某个结点(不包含结点本身)出发到叶子结点的黑色结点的数量,称为该节点x的黑深度,记为bd(x),例如bd(13) = 2,bd(8) =2 
内部结点——红黑树的非终结点
外部结点——红黑树的叶子节点

需要注意的是:

1. 叶子节点是nil的黑色结点

2. 特性5保证了,最大路径不会操作最小路径的2倍长度,维持了红黑树的平衡。

例如: 图中,红黑树的根节点的黑深度是2,那么这个红黑树的可能的最短路径(从根节点到叶子节点的路径上没有红色结点)是2,因为红色节点的子结点不能是红包结点,只能是黑色结点,而且叶子节点只能是黑色节点,那么,从根节点到叶子节点的路径上,最多只能有2个红色结点,也就是最长路径是4,这样,就保证了红黑树的平衡。

红黑树的着色和旋转

着色

红黑树的着色,将节点的颜色改变,以满足2,3,4,5特性

左旋

通常是树向右倾斜时候,进行左旋操作

右旋

当树向左倾斜,进行右旋操作。

 

红黑树的添加

-- 插入结点时候,结点颜色的影响?
如果插入一个黑色节点,根据特性5,从结点到叶子节点的黑色结点数量必须要一致,此时需要调整。
如果插入一个红色节点,根据特性4,红色节点的子节点必须是黑色结点。

--- 如此,假设插入的结点是红色的。

新插入的结点是红色的,如果遇到父结点是黑色的,修复操作结束,也就是说只有父节点是红色的时候,需要进行修复。

当父节点是红色的,下面几种情况需要修复操作

1. 叔叔结点是红色

2. 叔叔结点为空,并且祖父结点,父节点,新结点在一条斜线上

3. 叔叔结点为空,并且祖父结点,父节点,新结点不在一条斜线上

插入操作-case 1

case 1的操作是将父节点和叔叔节点与祖父节点的颜色互换,这样就符合了RBTRee的定义。即维持了高度的平衡,修复后颜色也符合RBTree定义的第三条和第四条。下图中,操作完成后A节点变成了新的节点。如果A节点的父节点不是黑色的话,则继续做修复操作。

插入操作-case 2

case 2的操作是将B节点进行右旋操作,并且和父节点A互换颜色。通过该修复操作RBTRee的高度和颜色都符合红黑树的定义。如果B和C节点都是右节点的话,只要将操作变成左旋就可以了。

插入操作-case 3

case 3的操作是将C节点进行左旋,这样就从case 3转换成case 2了,然后针对case 2进行操作处理就行了。case 2操作做了一个右旋操作和颜色互换来达到目的。如果树的结构是下图的镜像结构,则只需要将对应的左旋变成右旋,右旋变成左旋即可。

关于插入操作的总结

插入操作的修复过程是向Root结点回溯的过程,一旦涉及的结点符合红黑树的定义,修复结束.

删除操作

红黑树的删除工作首先也是和BST一样,如果是叶子节点直接删除,如果不是叶子节点需要找到后继节点,替换当前结点。

删除后,红黑树可能就不平衡了,需要进行修复操作。

删除修复操作,针对的是删除的结点是黑色结点,删除黑色结点后,红黑树变得不平衡,需要从兄弟结点上借掉,如果兄弟结点没有,只能向上一级回溯,将每一级的黑色结点都减少一个,当回溯到红色结点或者根节点时候,修复停止。

删除修复操作分为四种情况(删除黑节点后):

  1. 待删除的节点的兄弟节点是红色的节点。
  2. 待删除的节点的兄弟节点是黑色的节点,且兄弟节点的子节点都是黑色的。
  3. 待调整的节点的兄弟节点是黑色的节点,且兄弟节点的左子节点是红色的,右节点是黑色的(兄弟节点在右边),如果兄弟节点在左边的话,就是兄弟节点的右子节点是红色的,左节点是黑色的。
  4. 待调整的节点的兄弟节点是黑色的节点,且右子节点是是红色的(兄弟节点在右边),如果兄弟节点在左边,则就是对应的就是左节点是红色的。

删除操作-case 1

由于兄弟节点是红色节点的时候,无法借调黑节点,所以需要将兄弟节点提升到父节点,由于兄弟节点是红色的,根据RBTree的定义,兄弟节点的子节点是黑色的,就可以从它的子节点借调了。

case 1这样转换之后就会变成后面的case 2,case 3,或者case 4进行处理了。上升操作需要对C做一个左旋操作,如果是镜像结构的树只需要做对应的右旋操作即可。

之所以要做case 1操作是因为兄弟节点是红色的,无法借到一个黑节点来填补删除的黑节点。

删除操作-case 2

case 2的删除操作是由于兄弟节点可以消除一个黑色节点,因为兄弟节点和兄弟节点的子节点都是黑色的,所以可以将兄弟节点变红,这样就可以保证树的局部的颜色符合定义了。这个时候需要将父节点A变成新的节点,继续向上调整,直到整颗树的颜色符合RBTree的定义为止。

case 2这种情况下之所以要将兄弟节点变红,是因为如果把兄弟节点借调过来,会导致兄弟的结构不符合RBTree的定义,这样的情况下只能是将兄弟节点也变成红色来达到颜色的平衡。当将兄弟节点也变红之后,达到了局部的平衡了,但是对于祖父节点来说是不符合定义4的。这样就需要回溯到父节点,接着进行修复操作。

删除操作-case 3

case 3的删除操作是一个中间步骤,它的目的是将左边的红色节点借调过来,这样就可以转换成case 4状态了,在case 4状态下可以将D,E节点都阶段过来,通过将两个节点变成黑色来保证红黑树的整体平衡。

之所以说case-3是一个中间状态,是因为根据红黑树的定义来说,下图并不是平衡的,他是通过case 2操作完后向上回溯出现的状态。之所以会出现case 3和后面的case 4的情况,是因为可以通过借用侄子节点的红色,变成黑色来符合红黑树定义4.

删除操作-case 4

Case 4的操作是真正的节点借调操作,通过将兄弟节点以及兄弟节点的右节点借调过来,并将兄弟节点的右子节点变成红色来达到借调两个黑节点的目的,这样的话,整棵树还是符合RBTree的定义的。

Case 4这种情况的发生只有在待删除的节点的兄弟节点为黑,且子节点不全部为黑,才有可能借调到两个节点来做黑节点使用,从而保持整棵树都符合红黑树的定义。

删除操作的总结

红黑树的删除操作是最复杂的操作,复杂的地方就在于当删除了黑色节点的时候,如何从兄弟节点去借调节点,以保证树的颜色符合定义。由于红色的兄弟节点是没法借调出黑节点的,这样只能通过选择操作让他上升到父节点,而由于它是红节点,所以它的子节点就是黑的,可以借调。

对于兄弟节点是黑色节点的可以分成3种情况来处理,当所以的兄弟节点的子节点都是黑色节点时,可以直接将兄弟节点变红,这样局部的红黑树颜色是符合定义的。但是整颗树不一定是符合红黑树定义的,需要往上追溯继续调整。

对于兄弟节点的子节点为左红右黑或者 (全部为红,右红左黑)这两种情况,可以先将前面的情况通过选择转换为后一种情况,在后一种情况下,因为兄弟节点为黑,兄弟节点的右节点为红,可以借调出两个节点出来做黑节点,这样就可以保证删除了黑节点,整棵树还是符合红黑树的定义的,因为黑色节点的个数没有改变。

红黑树的删除操作是遇到删除的节点为红色,或者追溯调整到了root节点,这时删除的修复操作完毕。

总结

红黑树是平衡二叉树的一种实现,通过给结点的颜色来保持树的平衡。红黑树在删除、插入操作之前都是平衡的,删除、插入操作如果破坏了红黑树的平衡,就需要通过着色、旋转的方式维持平衡。

整个红黑树的查找,插入和删除都是O(logN)的,原因就是整个红黑树的高度是logN,查找从根到叶,走过的路径是树的高度,删除和插入操作是从叶到根的,所以经过的路径都是logN。

猜你喜欢

转载自blog.csdn.net/Ditto_zhou/article/details/83310369