Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

The-Art-Of-Programming-By-July/03.01.md at master · julycoding/The-Art-Of-Programming-By-July (github.com)

红黑树本质上是一棵近似平衡的二叉查找树

红黑树的查找、插入、删除的时间复杂度最坏为O(log n)

回顾:二叉查找树

  • 若任意结点的左子树不空,则子树上所有结点的值均小于它的结点的值;
  • 若任意结点的右子树不空,则子树上所有结点的值均大于它的结点的值;
  • 任意结点的左、右子树也分别为二叉查找树。
  • 没有键值相等的结点(no duplicate nodes)。

红黑树性质

红黑树的5条性质:

  • 每个结点要么是红的,要么是黑的。
  • 根结点是黑的。
  • 每个叶结点(叶结点即指树尾端NIL指针)是黑的。
  • 如果一个结点是红的,那么它的俩个儿子都是黑的。
  • 对于任一结点而言,其到叶结点的每一条路径都包含相同数目的黑结点。

正是红黑树的这5条性质,使得一棵n个结点是红黑树始终保持了logn的高度

img

旋转

通过对结点进行重新着色,以及对树进行相关的旋转操作,即修改树中某些结点的颜色及指针结构,来达到对红黑树进行插入或删除结点等操作后,继续保持它的性质或平衡。

左旋

在某个结点x上做左旋操作:以x到y(x的右孩子)之间的链为“支轴”进行,它使y成为该孩子树新的根,y的左孩子b成为x的右孩子

#p[v]是v的父亲,left[v]right[v]是v的左右孩子
def LEFT-ROTATE(T, x): #在x上做左旋 
    y ← right[x]
    
    #modify3
    right[x] ← left[y] 
    p[left[y]] ← x  
    
    #modify1
    p[y] ← p[x] 
    if p[x] = nil[T] #x是根
       then root[T] ← y  
    else if x = left[p[x]] #x是其父亲的左孩子 
        then left[p[x]] ← y  
    else right[p[x]] ← y  #x是其父亲的右孩子 
    
    #modify2
    left[y] ← x 
    p[x] ← y  

右旋

在某个结点x上做右旋操作:以x到y(x的左孩子)之间的链为“支轴”进行,它使y成为该孩子树新的根,y的右孩子c成为x的左孩子

红黑树的插入

红黑树的插入相当于在二叉查找树插入的基础上,为了重新恢复平衡,继续做了插入修复操作。

向红黑树T中插入z:

  • 搜索二叉树插入z
  • 把z的左孩子、右孩子都赋为叶结点nil,再把z结点着为红色
  • 旋转和调整着色
RB-INSERT(T, z)  
# 搜索二叉树插入
 1  y ← nil[T]  
 2  x ← root[T]  
 3  while x ≠ nil[T]  
 4      do y ← x  
 5         if key[z] < key[x]  
 6            then x ← left[x]  
 7            else x ← right[x]  
 8  p[z] ← y  
 9  if y = nil[T]  
10     then root[T] ← z  
11     else if key[z] < key[y]  
12             then left[y] ← z  
13             else right[y] ← z  
# 涂色
# 把z的左孩子、右孩子都赋为叶结点nil,再把z结点着为红色
14  left[z] ← nil[T]  
15  right[z] ← nil[T]  
16  color[z] ← RED  
# 旋转和调整着色
17  RB-INSERT-FIXUP(T, z)  

旋转和调整着色

RB-INSERT-FIXUP(T,z)
 1 while color[p[z]] = RED  
 2     do if p[z] = left[p[p[z]]]  
 3           then y ← right[p[p[z]]]  
 4                if color[y] = RED  
 5                   then color[p[z]] ← BLACK           
 6                        color[y] ← BLACK             
 7                        color[p[p[z]]] ← RED         
 8                        z ← p[p[z]]                   
 9                   else if z = right[p[z]]  
10                           then z ← p[z]                         
11                                LEFT-ROTATE(T, z)                
12                           color[p[z]] ← BLACK                   
13                           color[p[p[z]]] ← RED                  
14                           RIGHT-ROTATE(T, p[p[z]])              
15           else (same as then clause  
                         with "right" and "left" exchanged)  
16 color[root[T]] ← BLACK  

while当前节点的父结点着色还是红色的,就继续调整

 1 while color[p[z]] = RED

此时父结点的父结点一定存在,因为如果没有父结点即是根,而根不能是红色的

插入修复情况1:当前结点的父结点是红色且祖父结点的另一个子结点(叔叔结点)是红色

即如下代码所示:

 2     do if p[z] = left[p[p[z]]]  # 如果父亲是祖父的左孩子
 3           then y ← right[p[p[z]]]  
 4                if color[y] = RED  

将当前结点的父结点和叔叔结点涂黑,祖父结点涂红,从祖父结点重新开始算法

 5                   then color[p[z]] ← BLACK             
 6                        color[y] ← BLACK                
 7                        color[p[p[z]]] ← RED            
 8                        z ← p[p[z]]                     

当前一定是红色

评论