紅黑樹核心程式碼分析(JAVA)

不會近視的小何發表於2020-11-29

紅黑色的五條性質

(1)每個節點或者是黑色,或者是紅色。
(2)根節點是黑色。
(3)每個葉子節點(NIL)是黑色。 [注意:這裡葉子節點,是指為空(NIL或NULL)的葉子節點!]
(4)如果一個節點是紅色的,則它的子節點必須是黑色的。
(5)從一個節點到該節點的子孫節點的所有路徑上包含相同數目的黑節點。

刪除之後的處理(removeAfter()方法的實現)

對於紅黑樹的刪除:因為對於度為二的節點最終都會轉化為刪除度為一或者刪除度為0的節點

分為兩種:

1.刪除紅色節點

對於刪除紅色節點的情況,因為刪除紅色節點不會影響紅黑樹的性質,因此刪除紅色節點不需要處理,直接刪除即可

//程式碼體現
if(isRed(node)){return;}       //紅色葉子節點(一種)
2.刪除黑色節點
  • 對於刪除黑色節點的情況分為三種情況:
  • 擁有兩個 RED 子節點的 BLACK 節點,不可能被直接刪除,因為會找到它的子節點替代刪除【即都會轉化為下面兩種情況的判斷】,因此不需要考慮這樣的情況
  • 刪除擁有一個 RED 子節點的BLACK 節點 【情況一】
  • 刪除BLACK 葉子節點 【情況二】
如下所示三種情況,分別刪除25,46 / 76(同種情況),88

在這裡插入圖片描述

對於上面只需要考慮兩種情況

情況一:擁有一個 RED 子節點的BLACK 節點的刪除

對於擁有一個red紅色子節點的黑色節點的刪除而言,只需要將替代被刪黑色節點的紅色的子節點染黑即可,這樣就能保證紅黑樹的第五條性質。
例如:
在這裡插入圖片描述
上圖表示刪除 60 這個黑色節點,即將78指向60的線指向60的子節點,再將47這個節點染黑,如下所示:
在這裡插入圖片描述

情況二:刪除的是BLACK 葉子節點

刪除BLACK葉子節點分為兩種情況,第一種情況為刪除根節點(根節點為黑色),直接刪除即可,不做任何處理,root = null。

第二種情況為刪除的不是根節點,即又分為兩種情況:

1.被刪除的節點的兄弟節點為黑色

2.被刪除的節點的兄弟節點為紅色 (且可以根據紅黑樹的性質得:該紅節點必定有黑色的子節點,這裡是為後面處理該情況埋下伏筆)

【補充:對於上面兩種情況,可能有人會問:存在沒有兄弟的情況嗎?答案是不可能,除非是 root 節點,上面已經考慮了。為什麼呢?因為這是根據紅黑樹的性質決定,比如第五條性質中:從一個節點到該節點的子孫節點的所有路徑上包含相同數目的黑節點。】

對於上面的兄弟節點為黑色的情況又分為兩種情況:一種是被刪除的兄弟節點有至少一個紅色子節點,一種是沒有紅色子節點(也可以是說沒有子節點)

一:對於被刪除的兄弟節點有至少一個紅色子節點的情況

  • 解決方案為:
  • 對被刪之後的情況進行旋轉【分為LL/LR/RL/RR】
  • 旋轉之後的中心節點繼承 被刪節點的 parent 的顏色
  • 旋轉之後的左右節點染成BLACK

例:刪除91 【這裡可以看出節點 91 刪完後 50 -44 -41為LL情況,對父節點:50進行右旋轉即可】
在這裡插入圖片描述
刪後 如下所示: 【註解:中心44繼承繼承 被刪節點的 parent 的顏色:BLACK,旋轉之後的左右節點染成BLACK】
在這裡插入圖片描述

二:對於被刪除的兄弟節點是黑色且該黑色兄弟節點也沒有紅色子節點的情況

  • 分為兩種情況
  • 1.被刪除節點的父節點為紅色的情況
  • 2.被刪除節點的父節點為黑色的情況

對於被刪除節點的父節點為紅色的情況

  • 解決方案
  • 1.父節點染黑----> black(parent);
  • 2.黑兄弟染紅-----> red(sibling);

如下圖所示刪除95 【 black(parent); red(sibling); 】
在這裡插入圖片描述

刪除95後
在這裡插入圖片描述

對於被刪除節點的父節點為黑色的情況 【會導致parent 下溢】

  • 解決方案
  • 1.和上面一樣操作
  • 2.因為導致parent下溢,這時只需要將 parent 當成被刪除的節點處理即可【遞迴呼叫】

被刪除的節點的兄弟節點為紅色

  • 解決方案
  • 兄弟節點染成BLACK,parent 染成 RED ,進行旋轉
  • 於是又回到兄弟節點為BLACK的情況

在這裡插入圖片描述
對於上面的情況,刪除86這個節點,該節點的兄弟節點為 10 紅色節點,假如我們可以讓 86 的父節點 81 的左left 指向31,這樣我們就回到了上面 86 兄弟節點為黑色的情況!【妙啊~~~】

如何實現呢?
我們想到了旋轉!,對81這個父節點右旋轉,即可讓81的左孩子指標指向 31 這個節點,其他情況以此類推~

removeAfter()完整程式碼:

    public void removeAfter(Red_Node node,Red_Node replacement){
        if(isRed(node)){return;}       //紅色葉子節點(一種)
        if(isRed(replacement)){        //度為一的節點(兩種)
            black(replacement);
            return;
        }


        /***            “ 刪除黑色的情況 ”
         * 最後一種情況:度為 " 一 " 且 被刪除的節點顏色為黑色的情況
         * 看被刪除的節點的兄弟節點的顏色判斷進行是否可以借用或者進行下溢
         * sibling : 兄弟節點
         * 當黑色的節點存在時,必定在同階的高度中有節點,即:----> 黑節點一定存在兄弟節點
         */
        Red_Node parent = node.parent;
        if(parent == null){
            return;         //刪除的是根節點【度為一且為黑】,也是遞迴出口!!!
        }
        boolean left = parent.left ==null;
        Red_Node sibling = left ? parent.right:parent.left;
        /**
         * 因為旋轉的方向不同,分為被刪除的節點的位置不一樣,在右邊和左邊的情況
         */
        if(left){        //被刪除的節點在左邊,兄弟節點在右邊

            if(isRed(sibling)){
                red(parent);
                black(sibling);
                while_left(parent);
                sibling = parent.right;
            }
            //兄弟節點必定是黑色的

            if(isBlack(sibling.left) && isBlack(sibling.right)){   //兄弟節點的左右節點都是黑色!且當子節點是null時預設為黑色
                boolean parentBlack = isBlack(parent);
                black(parent);
                red(sibling);
                if(parentBlack){
                    removeAfter(parent,null);
                }
            }else {          //兄弟節點中至少有一個子節點為紅色【即可以被node節點借!!】
                if(isBlack(sibling.right)){
                    while_right(sibling);
                    sibling = parent.right;
                }
                color(sibling,colorOf(parent));
                black(sibling.right);
                black(parent);
                while_left(parent);
            }

        }else {        //被刪除的節點在右邊,兄弟節點在左邊
            /**
             * 先處理兄弟是紅色的情況 --- > 轉為黑色情況
             * 後面直接處理黑色情況即可
             */
            if(isRed(sibling)){
                red(parent);
                black(sibling);
                while_right(parent);
                sibling = parent.left;
            }
            //兄弟節點必定是黑色的

            if(isBlack(sibling.left) && isBlack(sibling.right)){   //兄弟節點的左右節點都是黑色!且當子節點是null時預設為黑色
                boolean parentBlack = isBlack(parent);
                black(parent);
                red(sibling);
                if(parentBlack){
                    removeAfter(parent,null);
                }
            }else {          //兄弟節點中至少有一個子節點為紅色【即可以被node節點借!!】
                if(isBlack(sibling.left)){
                    while_left(sibling);
                    sibling = parent.left;
                }
                color(sibling,colorOf(parent));
                black(sibling.left);
                black(parent);
                while_right(parent);
            }

        }
    }

待續……

紅黑樹完整程式碼:https://blog.csdn.net/m0_46542703/article/details/110354479

相關文章