紅黑樹

紅黑樹node

  1. 性質

    • node.color == (red | black)
    • root.color == black
    • node.color == red -> node.lchild.color == node.rchild.color == black and node.parent.color == black
    • NULL == black
    • length_to_nulls(node).same() == true
  2. 結構體成員

    • parentpython

    • color函數

    • lchildui

    • rchild3d

    • keycode

      class rbtree:
          def __init__(self,key):
              self.parent = None
              self.color = red
              self.lchild = self.rchild = None
              self.key = key
  3. 名詞

    • **黑高: **blog

      • 是棵紅黑樹: 知足上面五個性質。繼承

      • bh(node) == node_to_null_with_n_black_node(node)ip

        • 是棵紅黑樹it

        • 標記的是向下遍歷直到有幾個NULL

          blackhigh = 0
          node = node.lchild
          while node != None:
              if node.color == BLACK:
                  blackhigh += 1
              node = node.lchild
  4. 旋轉

    • right_rotate

      def right_rotate(T,y):
          x = y.lchild
          y.lchild = x.rchild
          if x.rchild != None:
              x.rchild.parent = y
          x.p = y.p
          if x.p == None:
              T.root = x
          elif y == y.parent.lchild:
              y.parent.lchild = x
          else:
              y.parent.rchild = x
          x.rchild = y
          y.p = x
    • lchlid_rotate

      def lchlid_rotate(T,x):
          y = x.rchild
          # 先將y存儲到變量中
          x.rchild = y.lchlid
          # 進行重鏈接操做,將y.lchlid過繼給剛失去右孩子的 x .
          if y.lchlid != None:
              # 若是y.lchlid不爲空,就告訴 y.lchlid 它的新的父親是誰
              y.lchlid.p = x
          y.p = x.p
          # 執行 y 由於上位了,也許要知道本身的父親(上司更好)是誰
          if x.p == None:
              # 也就是 原來的 x 沒有父親,那就說明是根節點
              T.root = y
          elif x == x.p.lchlid:
              # 若是 原來的 x 是左孩子,y 頂替上來也應該是左孩子
              x.p.lchlid = y
          else:
              # 若是 原來的 x 是右孩子,y 頂替上來也應該是右孩子
              x.p.rchild = y
          y.lchlid = x
          x.p = y
          # 而後執行最後的過繼操做
  5. 插入

    • new_node

      node = rbtree(key)
      node.lchild = node.rchild = None
      node.color = red
    • insert就是替換一個None

      def rb_insert(T,z):
          y = None
          # 建立一個 x y模型的迭代器,賦予初始值。y能夠當作x的父節點
          x = T.root
          # 從 根開始搜索,肯定須要插入的位置
          while x != None:
              y = x
              if z.key < x.key:
                  x = x.lchlid
              else:
                  x = x.rchlid
          # --------------------------------------------------------------------------
          # 這個循環用於查找插入位置,直到找到插入位置位置,y記錄插入位置。
          z.p = y
          # 找到插入位置了,那麼就先告訴 z 他的父親是誰,這裏是 y
          if y == None:
              # 若是 y == None ,說明是空樹
              T.root = z
              # 那麼 z 做爲新的根節點
          elif z.key < y.key:
              # 若是key小於,則插入右子樹
              y.lchlid = z
          else:
              # 不然插入左子樹
              y.rchlid = z
          # 在上面的while循環中,大小已經肯定了,插入的位置也必定是空,只是沒有記錄是左子樹仍是
          # 右子樹而已,這裏從新判斷一下。
          z.lchlid = None
          z.rchlid = None
          z.color = red
          # 對新添加的節點進行着色,爲何是紅色?由於葉子節點`None`必定是黑色。
          rb_insert_fixup(T,z)
          # 最後維護紅黑平衡
    • if newnode.parent.color == red

      違背了node.color == red and node.parent.color == black

      就須要維護

    • 維護

      # 大寫表示黑
      # 小寫表示紅
      # g 表示 grandparent
      # p 表示 parent
      # u 表示 uncle
      # n 表示 new node
      '''
      1. 新插入節點確定是紅,由於這樣插入這樣的節點不會影響以前的紅黑平衡。
      2. 插入的節點替換的是以前的黑節點,使用一個紅節點+兩個黑節點的組合不會影響這一部分的平衡
      3. 父輩的關係
         父黑:
             爺爺紅:
                 叔叔黑:
                     不作任何操做
                     自平衡
                 叔叔紅:
                     不可能
                 因此這個狀況不須要作任何操做
             爺爺黑:
                 叔叔黑:
                 		不作任何操做
                 		自平衡
                 叔叔紅:
                      自平衡
                 因此不作任何操做
         因此父親黑不用作任何操做,只考慮父親紅的狀況
         父紅:
         	    爺爺黑:
         	    	叔叔紅:
         	    		case 1
         	    		父和叔叔變黑
         	    		爺爺變紅
         	    		黑高增長,須要向上回溯維護更上層的平衡
         	    	叔叔黑:
         	    		本身和父親位置相同,都是左子樹或者都是右子樹:
                          旋轉變色
                          case 3
                          自平衡,兩邊的黑高都不變
                      本身和父親位置不一樣,一左一右:
                      	旋轉,改變平衡狀態
                      	1. 若是不旋轉:
                      		那麼本身將經過旋轉過繼給爺爺。
                      		過繼過去致使原爺爺那一邊的黑高大於另外一邊,並且還沒法變色
                      	2. 旋轉
                      		case 2 變爲 case 3
         		爺爺紅:
         			不可能
      4. 孫子+爺爺+父親+叔叔
         排列組合有 2^4=16種
         其中去掉我出現不會打亂平衡的狀況剩下一些我出現會打算平衡的狀況。即父親爲黑,不會打亂平衡。
      
         由於 爺爺+父親+叔叔 是平衡的
         是因爲孫子的出現纔打亂這個平衡,因此就須要從新維持這個平衡
      
         若是孫子的出現,致使了不平衡,而後爲了維持平衡,
         致使了爺爺的顏色發生了變化,也就是 爺爺變紅,即爺爺的黑高變大
         就須要向上回溯,那麼爺爺的身份就變成了孫子,而後繼續維持平衡。這個時候就只須要簡單的
         維持三者的紅黑平衡了。
      
         旋轉操做都須要考慮新節點的位置
      
              case 1:
                     G            g
                    / \          / \
                   p   u  -->   P   U
                  /            /
                 n            n
      
              case 2:
                    G             G
                   / \           / \
                  p   U  -->    n   U
                   \           /
                    n         p
      
              case 3:
                      G           P
                     / \         / \
                    p   U  -->  n   g
                   /                 \
                  n                   U
      '''
      
      def rb_insert_fixup(T,n):
          while n.p.color == RED:
          # 一直向上回溯維護,直到平衡
              if n.p == n.p.p.left:
              # 若是 n 的 父節點 是 左節點
      
                  u = n.p.p.right
                  # n.p 是 left
                  # u   是 right
                  if u.color == RED:
                  # case 1 只需簡單的修改一下顏色
                      n.p.color = BLACK
                      u.color = BLACK
                      n.p.p.color = RED
                      # 這種着色 致使了 黑高 + 1
                      n = n.p.p
                      # 因此須要向上繼續維護
                  else:
                      if n == n.p.right:
                      # case 2 ,先坐旋轉,若是不旋轉,後面的右旋轉旋轉
                      # 則會影響兩邊的黑高平衡,最終致使兩邊不平衡
                          n = n.p
                          left_rotate(T,n)
                      # case 3 簡單旋轉就能夠維護平衡
                      n.p.color = BLACK
                      n.p.p.color = RED
                      right_rotate(t,n.p.p)
                      # 這個時候 n.p.color 爲 BLACK 則就能夠不用回溯了,中止了循環回溯
              else:
                  # 和上面的操做類似,旋轉相反,鏡像
                  pass
          T.root.color = BLACK
          # 可能回溯到根節點,根節點可能被修改成紅,所以每次都改。
      # 整個函數就是用於 插入後 致使的紅黑性質被破壞,而後經過循環回溯維護平衡。
      • case 1

        • 插入紅節點 z發現父節點是,叔叔也是紅,則變色,變爲case 2
    • case 2

      • 左右平衡,可是黑高升高,向上回溯。z就至關於新插入節點。
      • 發現父親爲紅叔叔爲黑。且和父節點的方向相反。須要旋轉。
    • case 3

      • 因爲以前父子爲紅,旋轉和嫁接不會影響黑高平衡。可是父子爲紅衝突。
      • 父子都同方向,此時的父子左右已經平衡。說明須要旋轉變色保持平衡。
      • 若是不平衡就過繼過去,就會形成混亂。
    • case 4:維護成功

      • 父子兄弟都紅,爺爺黑,則變色就行,影響局部黑高,不影響整體黑高。
      • 子紅,父紅,兄弟黑,則父子和兄弟沒法保持紅黑互斥,可使用紅色轉移,使得樹更加飽和,即旋轉變色。保持紅黑性質。
  6. 刪除

刪除是採用後繼來頂替要刪除的節點。而後維護節點平衡。

也就是用後繼來進行 偷樑換柱

而後再用後繼的rchild頂替。

  • 關聯節點

    • r
    • r.child
    • r.parent
    • r.bro
  • 形成不平衡由失去r,而後r.child頂崗形成的newR,r.parent,r.bro的不平衡

    首先原來的節點是紅色是不會影響黑高的。後面的不管什麼顏色頂替上來都不會影響。

  • 只有替換的是黑纔會失衡,須要旋轉變色平衡。

def rb_transplant(T,u,v):
    if u.p == T.nil:
    # 刪除的節點是根節點
        T.root = v
    elif u == u.p.left:
    # 刪除的節點是左子樹
        u.p.left = v
        # 頂替原來的左子樹,升級
    else:
    # 刪除的節點是右子樹
        u.p.right = v
        # 頂替右子樹
    v.p = u.p
    # 告訴頂替的節點他爹是誰
def rb_delete(T,z):
    y = z
    y_origional_color = y.color
    # y_origional_color 是用來記錄 z 生前是啥顏色
    if z.left == T.nil:
        x = z.right
        rb_transplant(T,z,z.right)
    elif z.right == T.nil:
        x = z.left
        rb_transplant(T,z,z.left)
    # 若是有一個子樹是空,只須要下面的節點來頂替就好了
    else:
        y = tree_minimum(z.right)
        # 獲取後繼
        y_origional_color = y.color
        # 可能爲黑可能爲紅,黑也就意味着,右子樹可能爲紅
        x = y.right
        
        if y.p == z:
            x.p = z
        else:
            rb_transplant(T,y,y.right)
            y.right = z.right
            y.right.p = y
        rb_transplant(T,z,y)
        y.left = z.left
        y.left.p = y
        y.color = z.color
        # 用後繼來頂替z的位置,就好像什麼都沒有發生同樣
        # 後繼的右子樹頂替y的位置,這個時候就須要維持平衡了
        # 若是原來是黑色的,那麼黑高就收到了影響,不知足紅黑樹定義,就須要維持穩定了。
        # 黑高-1了,x表示是新頂上來的
    if y_origional_color == BLACK:
        rb_delete_fixup(T,x)
# 若是原來的是個黑的,刪掉了則黑高-1,新的節點就必須的是黑色的。
# 若是頂上來的是紅色的,直接改一下顏色就能夠了,就直接平衡了
# 若是頂上來的是黑色的,這個時候黑高仍是-1了,那麼就須要向上回溯,讓上層來保持平衡
def rb_delete_fixup(T,x):
    while x != T.root and x.color == BLACK:
        if x == x.p.left:
        # 若是 x 是 左子樹
            w = x.p.right
            if w.color == RED:
            # 並且右子樹是紅色,那麼 x 的父節點就是黑色,並且X也是黑色
                w.color = BLACK
                x.p.color = RED
                left_rotate(T,x.p)
                w = x.p.right
                # 將這種演變成2,3,4的狀況,而後統一處理
            if w.left.color == BLACK and w.right.color == BLACK:
                # 由於x是黑色,w是黑色,w.l 和 w.r都是黑色,去除一層黑色,降黑高
                w.color = RED
                x = x.p
                # 維護三角平衡
            else :
                if w.right.color == BLACK:
                    w.left.color = BLACK
                    w.color = RED
                    right_rotate(T,w)
                    w = x.p.right
                w.color = x.p.color
                x.p.color = BLACK
                w.right.color = BLACK
                left_rotate(T,x.p)
                x = T.root
        else:
            '''相反'''
            pass
    x.color = BLACK
if w.color == RED:
            # 頂替的是黑色
            # x 是黑色
            # w 是紅色
            # x.p 是黑色
            # 那麼黑高至少爲3
            # 右邊是紅色,w的黑高至少爲2
            # 也就是說 c 必然不是None,確定有數據
            # A可能沒有數據是None
                w.color = BLACK
                x.p.color = RED
                left_rotate(T,x.p)
                w = x.p.right
                # 將 w 也就是兄弟節點變爲黑色
                # x 的父親節點變爲紅色
                # 坐旋轉
                # w 改變
                # 這樣是將case 1 轉換爲下面的 case 2
                # 通過這樣的轉換左邊的B不平衡,A可能爲空,也就是黑高爲1
                # 右邊的C黑高爲2
                # ABC不平衡,從新處理,轉到 case 2

if w.left.color == BLACK and w.right.color == BLACK:
            # w的兩個孩子都是 Black
            # 若是是由case 1轉換過來的,那麼 x 就多是空
            # 	x = x.p 則是回溯,這裏若是 x 爲空是由問題的,須要修改
            # 若是不是從case 1轉換過來的,也就是替換上來出現的。
            # 	那麼就是說,B左邊黑高原先爲2,右邊黑高也爲2,D確定不爲空
            #   這種就將 w 也就是兄弟節點變爲紅色,而後網上回溯,若是 b 是紅色,則變成黑色,
            #   B變成黑色,w 變成紅色,對於原來的w來講下降了本身的黑高
            #   對於b的左邊來講也下降了本身的黑高,可是經過這種 兩黑一紅,向上聚攏,總體黑高不變。
            #   對於B來講,算上B,黑高平衡。
            #   若是B是黑色的,那麼就須要向上回溯,又成了新的 case 1,2,3,4 重新回溯
                w.color = RED
                x = x.p

else:
        	# 不知足上面的條件必然有一個不知足黑色
        		if w.right.color == BLACK:
                # 若是 w 的 right 是黑色,那麼他的 left 就是紅色,以下圖
                # 若是是從 case 1 轉換過來,那麼此時的 A 多是黑色,C仍是紅色,則說明右邊的黑高
                # 	爲 2 ,那麼也就是說 A 不爲空黑高可能爲 2,E則有可能爲空
                # 	經過這種變色右旋的方式,左右兩邊保持了平衡,可是黑高沒有發生變化,重新回溯,變爲
                # 	case 2 了
                # 若是不是從case 1 轉換過來的
                # 	A可能爲空,左邊的x的黑高仍然仍是下降了,沒有獲得維護,簡單的右旋
                #	造成新的左右平衡,而後向上回溯。
                    w.left.color = BLACK
                    w.color = RED
                    right_rotate(T,w)
                    w = x.p.right

# 若是是從case 3 轉換過來的
    			#	則 c 可能爲空,並且C是黑色,由於C以前爲紅,是子節點頂替上來的,紅色的子節點
        		#   必然爲黑
            	#	上面的轉換後的圖,仍然知足下面4的起始的特性。
                #	這種w D繼承父節點 B的顏色
                #   父節點轉換爲黑色,左旋,增長黑高。E變爲黑色,同時增長黑高。左旋,平衡
                #   x = T.root 中斷
            	# 若是不是從case3 轉過來的
                # 	也就是說 E 爲紅,C可能爲黑,可能爲紅。E爲紅,則C爲空則是黑色,C爲紅則也沒有影響
                #   反正D確定是平衡的,E必定爲紅,C可能爲黑,可能爲紅,並且C也是平衡的。
                #	那麼w爲黑,右邊黑高平衡,至少爲2。
                #   因爲w立刻就要升級頂替上層的D
                #   原來的父節點降級,着色爲黑色,那麼就是說,坐旋轉以後,左邊升高,行程平衡。
                #   由於E確定爲紅,則不爲空,變色爲黑,右邊也增高,造成平衡。
                #   x = T.root 終止向上回溯。
				w.color = x.p.color
                x.p.color = BLACK
                w.right.color = BLACK
                left_rotate(T,x.p)
                x = T.root

'''
  Case 1 - left rotate at parent
 
      P               S
     / \             / \
    N   s    -->    p   Sr
       / \         / \
      Sl  Sr      N   Sl

Case 2 - sibling color flip (p could be either color here)
    (p)           (p)
    / \           / \
   N   S    -->  N   s
      / \           / \
     Sl  Sr        Sl  Sr

 Case 3 - right rotate at sibling
 (p could be either color here)

   (p)           (p)
   / \           / \
  N   S    -->  N   Sl
     / \             \
    sl  Sr            s
                       \
                        Sr
 Case 4 - left rotate at parent + color flips
 (p and sl could be either color here.
  After rotation, p becomes black, s acquires
  p's color, and sl keeps its color)

      (p)             (s)
      / \             / \
     N   S     -->   P   Sr
        / \         / \
      (sl) sr      N  (sl)
'''
相關文章
相關標籤/搜索