【資料結構與演算法】手撕紅黑樹

gonghr發表於2022-04-01

紅黑樹

定義

動機

  • 二叉查詢樹查詢、插入、刪除最壞情況時間複雜度可能退化為 O(n)

  • AVL 樹很好的限制了數的高度為 O(logn),插入、刪除、查詢的最壞時間複雜度均為 O(logn);但刪除操作最多需要做 O(logn) 次旋轉。

  • 紅黑樹是具有如下特點的二叉查詢樹:

    • 每個結點是紅色或黑色的

    • 根結點為黑色

    • 外結點為黑色(外界點即為 null

    • 如果一個結點時紅色,那麼它的孩子必須是黑色

    • 任一結點到外結點的路徑上,包含相同數目的黑結點

【資料結構與演算法】手撕紅黑樹

結點的黑高度:該結點到外結點的路徑上包含的黑結點數目

紅黑樹的黑高度:根結點的黑高度

性質

  • 若忽略紅結點而只考慮黑結點,則這棵樹是平衡的

  • 任何一條路徑上不能有兩個連續的紅結點。從任意結點觸發最長的路徑(紅黑結點間隔組成)是最短路徑(僅由黑結點組成)的 2

  • 任何一個結點的左右子樹的高度最多相差 2

  • 紅黑樹的平衡性比 AVL 樹更弱

  • 平均和最壞高度:O(logn)

  • 查詢、插入、刪除操作的平均和最壞時間複雜度是 O(logn),且僅涉及 O(1) 次旋轉

  • 紅黑樹的高度:h = O(logn)logn <= h <= 2logn
    【資料結構與演算法】手撕紅黑樹

結點定義

【資料結構與演算法】手撕紅黑樹

1️⃣ key:關鍵字的值
2️⃣ value:關鍵字的儲存資訊
3️⃣ parent:父親結點的引用
4️⃣ left:左子樹根結點的引用
5️⃣ right:右子樹根結點引用
6️⃣ color:結點顏色

class RBNode<K extends Comparable<K>, V> {
    public RBNode<K, V> parent;
    public RBNode<K, V> left;
    public RBNode<K, V> right;
    public boolean color;
    public K key;
    public V value;

    public RBNode(RBNode<K, V> parent, K key, V value) {
        this.parent = parent;
        this.key = key;
        this.value = value;
    }
}

插入演算法

  • 查詢,若查詢成功則不插入並更新結點值;若查詢失敗,再查詢失敗的位置插入新結點

  • 新結點總是作為葉結點插入的

  • 新結點必須為紅色

  • 若新結點的父結點是黑色,則插入過程結束

  • 若新結點的父結點是紅色,則需要處理雙紅缺陷

這裡定義 x 為新插入的結點,px 的父結點,gx 的爺爺結點,ux 的叔叔

【資料結構與演算法】手撕紅黑樹

雙紅修正

1️⃣ x 的叔叔是黑色

  • gx 的路徑為 LL
    【資料結構與演算法】手撕紅黑樹

  • gx 的路徑為 RR
    【資料結構與演算法】手撕紅黑樹

  • gx 的路徑為 LR
    【資料結構與演算法】手撕紅黑樹

  • gx 的路徑為 RL
    【資料結構與演算法】手撕紅黑樹

2️⃣ x 的叔叔是紅色

【資料結構與演算法】手撕紅黑樹

例項

【資料結構與演算法】手撕紅黑樹

此時結點 50 的父親結點 60 也為紅色,需要進行雙紅修正。注意到,其叔叔結點 20 也是紅色,只需要將父親結點 60 和叔叔結點 20 變為黑色,再把爺爺結點 30 變為紅色,結果如下圖。

【資料結構與演算法】手撕紅黑樹

之後會發現,結點 30 和其父結點 70 都為紅色,需要進行雙紅修正。而結點 30 的叔叔結點 85 為黑色,結點 30 的爺爺結點 15 到達結點 30 的路徑是 RL 型,故需要先右旋,再左旋。結果如下圖。

【資料結構與演算法】手撕紅黑樹

旋轉操作

由於紅黑樹結點還擁有 parent 屬性,故不能像平衡二叉樹一樣進行旋轉(【資料結構與演算法】手撕平衡二叉樹),需要特殊考慮 parent的賦值。

1️⃣ RR

左旋

【資料結構與演算法】手撕紅黑樹

需要特別注意的是爺爺結點 gparent 的設定,需要先判斷 g 是左子結點還是右子結點。

    private void leftRotate(RBNode<K, V> g) {
        if (g != null) {
            RBNode<K, V> p = g.right;
            g.right = p.left;
            if (p.left != null) {
                p.left.parent = g;
            }
            p.parent = g.parent;
            if (g.parent == null) {
                this.root = p;
            } else if (g.parent.left == g) {
                g.parent.left = p;
            } else {
                g.parent.right = p;
            }
            p.left = g;
            g.parent = p;
        }

2️⃣ LL

右旋

【資料結構與演算法】手撕紅黑樹

需要特別注意的是爺爺結點 gparent 的設定,需要先判斷 g 是左子結點還是右子結點。

    private void rightRotate(RBNode<K, V> g) {
        if(g != null) {
            RBNode<K, V> p = g.left;
            g.left = p.right;
            if(p.right != null) {
                p.right.parent = g;
            }
            p.parent = g.parent;
            if(g.parent == null) {
                this.root = p;
            } else if(g.parent.left == g) {
                g.parent.left = p;
            } else {
                g.parent.right = p;
            }
            p.right = g;
            g.parent = p;
        }
    }

3️⃣ LR

先對 g 的左子結點左旋,再對 g 右旋

【資料結構與演算法】手撕紅黑樹
    private void leftRightRotate(RBNode<K,V> g) {
        if(g != null) {
            leftRotate(g.left);
            rightRotate(g);
        }
    }

4️⃣ RL

先對 g 的右子結點右旋,再對 g 左旋

【資料結構與演算法】手撕紅黑樹
    private void rightLeftRotate(RBNode<K, V> g) {
        if(g != null) {
            rightRotate(g.right);
            leftRotate(g);
        }
    }

程式碼

1️⃣ 插入操作

程式碼描述

  • 如果根結點為空,則建立根結點,返回

  • 否則,根據 key 與結點關鍵值的比較找到插入位置

  • 建立結點並插入

  • 平衡處理

    public void insert(K key, V value) {
        RBNode<K, V> t = this.root;
        if (t == null) {
            this.root = new RBNode<K, V>(null, key, value);
            setColor(root, BLACK);
            return;
        }
        int cmp = 0;
        RBNode<K, V> parent = null;
        while (t != null) {
            parent = t;
            cmp = key.compareTo(t.key);
            if (cmp < 0) {
                t = t.left;
            } else if (cmp > 0) {
                t = t.right;
            } else {
                t.value = value;
                return;
            }
        }
        RBNode<K, V> e = new RBNode<K, V>(parent, key, value);
        if (cmp < 0) {
            parent.left = e;
        } else {
            parent.right = e;
        }
        // 平衡處理,旋轉+變色
        fixAfterPut(e);
    }

2️⃣ 平衡處理

程式碼描述

  • 新插入的結點都設定為紅色

  • 進入迴圈,條件是 x 不為空且 x 不為根結點且 x 的父親為紅色(進行雙紅處理)

  • 若父結點是爺爺結點的左子結點,即為 L 型別

    • uncle 為叔叔結點

    • 若叔叔結點為紅色:令爺爺結點為紅色,父親結點為黑色,叔叔結點為黑色,x 賦為爺爺結點,進入下一次迴圈處理。

    • 若叔叔結點為黑色:

      • x 是父結點的右子結點,則為 LR 型,先左旋,再右旋,染色

      • x 是父結點的左子結點,則為 LL 型,左旋,染色

  • 若父結點是爺爺結點的右子結點,即為 R 型別

    • uncle 為叔叔結點

    • 若叔叔結點為紅色:令爺爺結點為紅色,父親結點為黑色,叔叔結點為黑色,x 賦為爺爺結點,進入下一次迴圈處理。

    • 若叔叔結點為黑色:

      • x 是父結點的左子結點,則為 RL 型,先右旋,再左旋,染色

      • x 是父結點的右子結點,則為 RR 型,右旋,染色

  • 設定根結點為黑色(始終如此)

    private void fixAfterInsert(RBNode<K, V> x) {
        setColor(x, RED);
        while (x != null && x != root && colorOf(x.parent) == RED) {
            if (x.parent == x.parent.parent.left) {
                RBNode<K, V> uncle = x.parent.parent.right;
                if (colorOf(uncle) == RED) {
                    setColor(x.parent, BLACK);
                    setColor(uncle, BLACK);
                    setColor(x.parent.parent, RED);
                    x = x.parent.parent;
                } else {
                    if (x == x.parent.right) {
                        leftRightRotate(x.parent.parent);
                        setColor(x, BLACK);
                        setColor(x.right, RED);
                    } else {
                        setColor(x.parent, BLACK);
                        setColor(x.parent.parent, RED);
                        rightRotate(x.parent.parent);
                    }
                }
            } else {
                RBNode<K, V> uncle = x.parent.parent.left;
                if (colorOf(uncle) == RED) {
                    setColor(x.parent, BLACK);
                    setColor(uncle, BLACK);
                    setColor(x.parent.parent, RED);
                    x = x.parent.parent;
                } else {
                    if (x == x.parent.left) {
                        rightLeftRotate(x.parent.parent);
                        setColor(x, BLACK);
                        setColor(x.left, RED);
                    } else {
                        setColor(x.parent, BLACK);
                        setColor(x.parent.parent, RED);
                        leftRotate(x.parent.parent);
                    }
                }
            }
        }
        setColor(root, BLACK);
    }

查詢前驅和後繼結點

1️⃣ 查詢前驅結點

  • 若當前結點為空,則返回

  • 若當前結點存在左子樹,則前驅結點是左子樹的最右結點

  • 若當前結點不存在左子樹:注意這種情況在刪除時是不用考慮的,要麼為葉子結點,可以直接刪除;要麼可以用右子樹代替,不用考慮前驅。這裡是求的是嚴格意義上的前驅結點。
    【資料結構與演算法】手撕紅黑樹

    private RBNode<K, V> predecessor(RBNode<K, V> node) {
        if (node == null) {
            return null;
        } else if (node.left != null) {
            RBNode<K, V> p = node.left;
            while (p.right != null) {
                p = p.right;
            }
            return p;
        } else {
            RBNode<K, V> p = node.parent;
            RBNode<K, V> ch = node;
            while (p != null && ch == p.left) {
                ch = p;
                p = p.parent;
            }
            return p;
        }
    }

2️⃣ 查詢後繼結點

  • 若當前結點為空,則返回

  • 若當前結點存在右子樹,則前驅結點是右子樹的最左結點

  • 若當前結點不存在右子樹:注意這種情況在刪除時是不用考慮的,要麼為葉子結點,可以直接刪除;要麼可以用左子樹代替,不用考慮後繼。這裡是求的是嚴格意義上的後繼結點。
    【資料結構與演算法】手撕紅黑樹

    private RBNode<K, V> successor(RBNode<K, V> node) {
        if (node == null) {
            return null;
        } else if (node.right != null) {
            RBNode<K, V> p = node.right;
            while (p.left != null) {
                p = p.left;
            }
            return p;
        } else {
            RBNode<K, V> p = node.parent;
            RBNode<K, V> ch = node;
            while (p != null && ch == p.right) {
                ch = p;
                p = p.parent;
            }
            return p;
        }
    }

刪除演算法

【資料結構與演算法】手撕紅黑樹
  • 定義 x 為實際刪除結點,r 為替換 X 的結點,px 的父親,sx 的兄弟

  • 查詢,若查詢失敗則直接返回,若查詢成功則刪除對應的結點 x

  • 刪除操作最終可歸結為兩種情況:刪除葉結點和刪除只有一個孩子的結點

    • x 為紅,則必為紅葉子,直接刪除

    • x 為黑 r 為紅,r 替換 x,並將 r 染黑

    • x 為黑 r 為黑:需要處理雙黑缺陷

雙黑缺陷

【資料結構與演算法】手撕紅黑樹
  • x 為黑 r 為黑(r 可能為外結點):雙黑缺陷

  • x 為實際刪除結點,r 為替換 x 的結點,px 的父親,sx 的兄弟,ns 的孩子

1️⃣ 兄弟 s 為黑,且有紅孩子

  • pn 的路徑為 LL
    【資料結構與演算法】手撕紅黑樹

  • pn 的路徑為 LR
    【資料結構與演算法】手撕紅黑樹

  • pn 的路徑為 RR 型。
    【資料結構與演算法】手撕紅黑樹

  • pn 的路徑為 RL 型。
    【資料結構與演算法】手撕紅黑樹

2️⃣ 兄弟 s 為黑,無紅孩子

  • 父親 p 為紅
    【資料結構與演算法】手撕紅黑樹

  • 父親 p 為黑
    【資料結構與演算法】手撕紅黑樹

3️⃣ 兄弟 s 為紅

  • sp 的左孩子
    【資料結構與演算法】手撕紅黑樹

經過變換後,問題沒有解決,但 r 的兄弟變為黑色,可能轉為黑兄弟有紅孩子情況(最多需兩次旋轉),或黑兄弟無紅孩子有紅父親情況,需染色。

  • sp 的右孩子
    【資料結構與演算法】手撕紅黑樹

經過變換後,問題沒有解決,但 r 的兄弟變為黑色,可能轉為黑兄弟有紅孩子情況(最多需兩次旋轉),或黑兄弟無紅孩子有紅父親情況,需染色。

程式碼

1️⃣ 刪除時先找到要刪除的結點

    private RBNode<K, V> getNode(K key) {
        RBNode<K, V> node = this.root; 
        while (node != null) {
            int cmp = key.compareTo(node.key);
            if (cmp < 0) {
                node = node.left;
            } else if (cmp > 0) {
                node = node.right;
            } else {
                return node;
            }
        }
        return null;
    }

2️⃣ 刪除結點

程式碼描述

  • 若要被刪除的結點的左右子結點都存在,則找到其中序後繼(前驅)結點,將中序結點的值賦給該結點,然後令該結點的引用指向中序結點,用來刪除該中序結點

  • relpacement 指向結點的非空子結點

  • replacemnet 不為空,即找到了該結點的非空子結點,則用該子結點結點替換該結點。特別注意 parent 屬性的賦值。若該結點是黑色,處理雙黑缺陷。

  • 若該結點沒有子結點,且父結點為空,說明該結點就是根節點,直接刪除

  • 若該結點沒有子結點,且父結點不是空

    • 若為黑色結點,處理雙黑缺陷

    • 刪除該結點

    private void deleteNode(RBNode<K, V> node) {
        if (node.left != null && node.right != null) {
//            RBNode<K, V> predecessor = predecessor(node);
//            node.value = predecessor.value;
//            node = predecessor;
            RBNode<K, V> successor = successor(node);
            node.key = successor.key;
            node.value = successor.value;
            node = successor;
        }
        RBNode<K, V> replacement = node.left != null ? node.left : node.right;
        if (replacement != null) {
            replacement.parent = node.parent;
            if (node.parent == null) {
                root = replacement;
            } else if (node == node.parent.left) {
                node.parent.left = replacement;
            } else {
                node.parent.right = replacement;
            }
            node.left = node.right = node.parent = null;
            if (colorOf(node) == BLACK) {
                fixAfterRemove(replacement);
            }
        } else if (node.parent == null) {
            root = null;
        } else {
            if (colorOf(node) == BLACK) {
                fixAfterRemove(node);
            }
            if (node.parent != null) {
                if (node == node.parent.left) {
                    node.parent.left = null;
                } else {
                    node.parent.right = null;
                }
            }
        }
    }

3️⃣ 處理雙黑缺陷

程式碼描述

  • 只要當前結點不是根節點,且顏色為黑色,迴圈

    • 如果當前結點是左節點,即為 L

      • s 為該結點的右兄弟結點

      • s 為紅色,說明該結點的兄弟結點為紅色,屬於情況三(2),將其轉化為情況一或二(兄弟結點 s 變為黑)

      • s 為黑色,且無紅孩子,屬於情況二(將兄弟結點 s 染紅),將 x 賦予 x.parent 向上繼續雙黑修正

      • 否則,若有紅孩子

        • 若為 LL 型,右旋 + 染色,屬於情況一(1)

        • 若為 LR 型,左右旋 + 染色,屬於情況一(2)

    • 如果當前結點是右節點,即為 R

      • s 為該結點的左兄弟結點

      • s 為紅色,說明該結點的兄弟結點為紅色,屬於情況三(1),將其轉化為情況一或二(兄弟結點 s 變為黑)

      • s 為黑色,且無紅孩子,屬於情況二(將兄弟結點 s 染紅),將 x 賦予 x.parent 向上繼續雙黑修正

      • 否則,若有紅孩子

        • 若為 RR 型,左旋 + 染色,屬於情況一(3)

        • 若為 RL 型,右左旋 + 染色,屬於情況一(4)

  • x 有可能最後指向根結點,必須保證為黑色

    private void fixAfterRemove(RBNode<K, V> x) {
        while (x != root && colorOf(x) == BLACK) {
            if (x == x.parent.left) {
                RBNode<K, V> s = x.parent.right;
                if (colorOf(s) == RED) {
                    leftRotate(x.parent);
                    setColor(s, BLACK);
                    setColor(x.parent, RED);
                    s = x.parent.right;
                }
                if (colorOf(s.left) == BLACK && colorOf(s.right) == BLACK) {
                    setColor(s, RED);
                    x = x.parent;
                } else {
                    if (colorOf(s.right) == BLACK) {
                        RBNode<K, V> p = s.parent;
                        rightLeftRotate(p);
                        setColor(s, BLACK);
                        setColor(s.parent, p.color);
                        setColor(p, BLACK);
                        x = root;
                    } else {
                        RBNode<K, V> p = s.parent;
                        leftRotate(p);
                        setColor(s, colorOf(p));
                        setColor(p, BLACK);
                        setColor(s.right, BLACK);
                        x = root;  // 終止迴圈
                    }
                }
            } else {
                RBNode<K, V> s = x.parent.left;
                if (colorOf(s) == RED) {
                    setColor(s, BLACK);
                    setColor(x.parent, RED);
                    rightRotate(x.parent);
                    s = x.parent.left;
                }
                if (colorOf(s.left) == BLACK && colorOf(s.right) == BLACK) {
                    setColor(s, RED);
                    x = x.parent;
                } else {
                    if (colorOf(s.left) == BLACK) {
                        RBNode<K, V> p = s.parent;
                        leftRightRotate(p);
                        setColor(s, BLACK);
                        setColor(s.parent, p.color);
                        setColor(p, BLACK);
                        x = root;
                    } else {
                        RBNode<K, V> p = s.parent;
                        rightRotate(p);
                        setColor(s, colorOf(p));
                        setColor(p, BLACK);
                        setColor(s.left, BLACK);
                        x = root;
                    }
                }
            }
        }
        setColor(x, BLACK);
    }

總結

最多涉及 3 次旋轉,O(logn) 次染色

AVL 樹 vs 紅黑樹

  • 查詢、插入、刪除最壞時間複雜度均為 O(logn)

  • 紅黑樹平衡性弱於 AVL 樹,故查詢效能低於 AVL 樹。

  • 紅黑樹插入刪除所需的旋轉次數較少,插入、刪除效率高於 AVL 樹。

完整程式碼

class RBTree<K extends Comparable<K>, V> {
    private static final boolean RED = false;
    private static final boolean BLACK = true;
    private RBNode<K, V> root;

    private void inorder(RBNode<K, V> root) {
        if (root != null) {
            inorder(root.left);
            System.out.print(root.key + " " + (root.color ? "B" : "R") + " ");
            inorder(root.right);
        }
    }

    private void preorder(RBNode<K, V> root) {
        if (root != null) {
            System.out.print(root.key + " " + (root.color ? "B" : "R") + " ");
            preorder(root.left);
            preorder(root.right);
        }
    }

    public void preorderTraverse() {
        preorder(this.root);
        System.out.println();
    }

    public void inorderTraverse() {
        inorder(this.root);
        System.out.println();
    }

    public boolean colorOf(RBNode<K, V> node) {
        return node != null ? node.color : BLACK;
    }

    public void insert(K key, V value) {
        RBNode<K, V> t = this.root;
        if (t == null) {
            this.root = new RBNode<K, V>(null, key, value);
            setColor(root, BLACK);
            return;
        }
        int cmp = 0;
        RBNode<K, V> parent = null;
        while (t != null) {
            parent = t;
            cmp = key.compareTo(t.key);
            if (cmp < 0) {
                t = t.left;
            } else if (cmp > 0) {
                t = t.right;
            } else {
                t.value = value;
                return;
            }
        }
        RBNode<K, V> e = new RBNode<K, V>(parent, key, value);
        if (cmp < 0) {
            parent.left = e;
        } else {
            parent.right = e;
        }
        // 平衡處理,旋轉+變色
        fixAfterInsert(e);
    }

    private void fixAfterInsert(RBNode<K, V> x) {
        setColor(x, RED);
        while (x != null && x != root && colorOf(x.parent) == RED) {
            if (x.parent == x.parent.parent.left) {
                RBNode<K, V> uncle = x.parent.parent.right;
                if (colorOf(uncle) == RED) {
                    setColor(x.parent, BLACK);
                    setColor(uncle, BLACK);
                    setColor(x.parent.parent, RED);
                    x = x.parent.parent;
                } else {
                    if (x == x.parent.right) {
                        leftRightRotate(x.parent.parent);
                        setColor(x, BLACK);
                        setColor(x.right, RED);
                    } else {
                        setColor(x.parent, BLACK);
                        setColor(x.parent.parent, RED);
                        rightRotate(x.parent.parent);
                    }
                }
            } else {
                RBNode<K, V> uncle = x.parent.parent.left;
                if (colorOf(uncle) == RED) {
                    setColor(x.parent, BLACK);
                    setColor(uncle, BLACK);
                    setColor(x.parent.parent, RED);
                    x = x.parent.parent;
                } else {
                    if (x == x.parent.left) {
                        rightLeftRotate(x.parent.parent);
                        setColor(x, BLACK);
                        setColor(x.left, RED);
                    } else {
                        setColor(x.parent, BLACK);
                        setColor(x.parent.parent, RED);
                        leftRotate(x.parent.parent);
                    }
                }
            }
        }
        setColor(root, BLACK);
    }

    private RBNode<K, V> predecessor(RBNode<K, V> node) {
        if (node == null) {
            return null;
        } else if (node.left != null) {
            RBNode<K, V> p = node.left;
            while (p.right != null) {
                p = p.right;
            }
            return p;
        } else {
            RBNode<K, V> p = node.parent;
            RBNode<K, V> ch = node;
            while (p != null && ch == p.left) {
                ch = p;
                p = p.parent;
            }
            return p;
        }
    }

    private RBNode<K, V> successor(RBNode<K, V> node) {
        if (node == null) {
            return null;
        } else if (node.right != null) {
            RBNode<K, V> p = node.right;
            while (p.left != null) {
                p = p.left;
            }
            return p;
        } else {
            RBNode<K, V> p = node.parent;
            RBNode<K, V> ch = node;
            while (p != null && ch == p.right) {
                ch = p;
                p = p.parent;
            }
            return p;
        }
    }

    public void remove(K key) {
        RBNode<K, V> node = getNode(key);
        if (node == null) return;
        deleteNode(node);
        return;
    }

    private void deleteNode(RBNode<K, V> node) {
        if (node.left != null && node.right != null) {
//            RBNode<K, V> predecessor = predecessor(node);
//            node.value = predecessor.value;
//            node = predecessor;
            RBNode<K, V> successor = successor(node);
            node.key = successor.key;
            node.value = successor.value;
            node = successor;
        }
        RBNode<K, V> replacement = node.left != null ? node.left : node.right;
        if (replacement != null) {
            replacement.parent = node.parent;
            if (node.parent == null) {
                root = replacement;
            } else if (node == node.parent.left) {
                node.parent.left = replacement;
            } else {
                node.parent.right = replacement;
            }
            node.left = node.right = node.parent = null;
            if (colorOf(node) == BLACK) {
                fixAfterRemove(replacement);
            }
        } else if (node.parent == null) {
            root = null;
        } else {
            if (colorOf(node) == BLACK) {
                fixAfterRemove(node);
            }
            if (node.parent != null) {
                if (node == node.parent.left) {
                    node.parent.left = null;
                } else {
                    node.parent.right = null;
                }
            }
        }
    }

    private void fixAfterRemove(RBNode<K, V> x) {
        while (x != root && colorOf(x) == BLACK) {
            if (x == x.parent.left) {
                RBNode<K, V> s = x.parent.right;
                if (colorOf(s) == RED) {
                    leftRotate(x.parent);
                    setColor(s, BLACK);
                    setColor(x.parent, RED);
                    s = x.parent.right;
                }
                if (colorOf(s.left) == BLACK && colorOf(s.right) == BLACK) {
                    setColor(s, RED);
                    x = x.parent;
                } else {
                    if (colorOf(s.right) == BLACK) {
                        RBNode<K, V> p = s.parent;
                        rightLeftRotate(p);
                        setColor(s, BLACK);
                        setColor(s.parent, p.color);
                        setColor(p, BLACK);
                        x = root;
                    } else {
                        RBNode<K, V> p = s.parent;
                        leftRotate(p);
                        setColor(s, colorOf(p));
                        setColor(p, BLACK);
                        setColor(s.right, BLACK);
                        x = root;  // 終止迴圈
                    }
                }
            } else {
                RBNode<K, V> s = x.parent.left;
                if (colorOf(s) == RED) {
                    setColor(s, BLACK);
                    setColor(x.parent, RED);
                    rightRotate(x.parent);
                    s = x.parent.left;
                }
                if (colorOf(s.left) == BLACK && colorOf(s.right) == BLACK) {
                    setColor(s, RED);
                    x = x.parent;
                } else {
                    if (colorOf(s.left) == BLACK) {
                        RBNode<K, V> p = s.parent;
                        leftRightRotate(p);
                        setColor(s, BLACK);
                        setColor(s.parent, p.color);
                        setColor(p, BLACK);
                        x = root;
                    } else {
                        RBNode<K, V> p = s.parent;
                        rightRotate(p);
                        setColor(s, colorOf(p));
                        setColor(p, BLACK);
                        setColor(s.left, BLACK);
                        x = root;
                    }
                }
            }
        }
        setColor(x, BLACK);
    }

    private RBNode<K, V> getNode(K key) {
        RBNode<K, V> node = this.root;
        while (node != null) {
            int cmp = key.compareTo(node.key);
            if (cmp < 0) {
                node = node.left;
            } else if (cmp > 0) {
                node = node.right;
            } else {
                return node;
            }
        }
        return null;
    }

    private void setColor(RBNode<K, V> node, boolean color) {
        if (node != null) {
            node.color = color;
        }
    }

    private void leftRotate(RBNode<K, V> g) {
        if (g != null) {
            RBNode<K, V> p = g.right;
            g.right = p.left;
            if (p.left != null) {
                p.left.parent = g;
            }
            p.parent = g.parent;
            if (g.parent == null) {
                this.root = p;
            } else if (g.parent.left == g) {
                g.parent.left = p;
            } else {
                g.parent.right = p;
            }
            p.left = g;
            g.parent = p;
        }
    }

    private void rightRotate(RBNode<K, V> g) {
        if (g != null) {
            RBNode<K, V> p = g.left;
            g.left = p.right;
            if (p.right != null) {
                p.right.parent = g;
            }
            p.parent = g.parent;
            if (g.parent == null) {
                this.root = p;
            } else if (g.parent.left == g) {
                g.parent.left = p;
            } else {
                g.parent.right = p;
            }
            p.right = g;
            g.parent = p;
        }
    }

    private void leftRightRotate(RBNode<K, V> g) {
        leftRotate(g.left);
        rightRotate(g);
    }

    private void rightLeftRotate(RBNode<K, V> g) {
        rightRotate(g.right);
        leftRotate(g);
    }
}

class RBNode<K extends Comparable<K>, V> {
    public RBNode<K, V> parent;
    public RBNode<K, V> left;
    public RBNode<K, V> right;
    public boolean color;
    public K key;
    public V value;

    public RBNode(RBNode<K, V> parent, K key, V value) {
        this.parent = parent;
        this.key = key;
        this.value = value;
    }
}

? 方法測試

    public static void main(String[] args) {
        RBTree<Integer, Integer> tree = new RBTree<>();
        Scanner scanner = new Scanner(System.in);
        int n;
        n = scanner.nextInt();
        while (n-- > 0) {
            String operation = scanner.next();
            String value = scanner.next();
            if (operation.equals("Insert")) {
                tree.insert(Integer.parseInt(value), 1);
            } else {
                tree.remove(Integer.parseInt(value));
            }
        }
        tree.inorderTraverse();
        System.out.println();
        tree.preorderTraverse();
    }

輸入

32
Insert 10
Insert 40
Insert 30
Insert 60
Insert 90
Insert 70
Insert 20
Insert 50
Insert 80
Insert 10
Insert 66
Insert 85
Insert 60
Insert 12
Insert 32
Insert 74
Insert 7
Insert 52
Insert -5
Insert 13
Insert 23
Insert 13
Insert 103
Insert 306
Insert 2
Insert -97
Insert 752
Remove 90
Remove 60
Remove 70
Remove 50
Remove 80

輸出

-97 R -5 B 2 R 7 R 10 B 12 B 13 R 20 B 23 R 30 R 32 B 40 B 52 B 66 B 74 B 85 B 103 B 306 R 752 B 

66 B 30 R 12 B 7 R -5 B -97 R 2 R 10 B 20 B 13 R 23 R 40 B 32 B 52 B 85 B 74 B 306 R 103 B 752 B 

相關文章