數據結構--紅黑樹(java)

算法與數據結構系列源碼:https://github.com/ThinerZQ/AllAlgorithmInJava
本篇博客源碼下載:https://github.com/ThinerZQ/AllAlgorithmInJava/blob/master/src/main/java/com/zq/algorithm/tree/RedBlackTreeTest.java

什麼是二叉查找樹
是不是某一顆子樹的根節點左邊的節點都小於根節點,右邊的的節點都大於根節點。

什麼是紅黑樹?
想想如果二叉查找樹插入的元素是有序的,那麼二叉查找樹的高是不是就是n 了(元素個數),那麼二叉查找樹的時間複雜度是不是就是o(h)=o(n) 了
而紅黑樹是一種“平衡”查找樹中的一種,可以保證在最壞的情況下基本動態結合操作的時間複雜度爲o(lgn)

怎麼保證呢?我們給紅黑樹一個嚴格的定義就好了嘛

定義:

紅黑樹是一顆二叉查找樹,他在每個節點上增加了一個存儲位來表示節點的顏色,可以是Red,或Black。通過對任何一條從根到葉子的簡單路徑上各個節點的顏色值進行約束,紅黑樹確保沒有一條路徑會比其他路徑長出2被,因而是近似於平衡的。怎麼約束呢?\


約束(性質):

  1. 每個節點或是紅色的,或是黑色的。
  2. 根節點是黑色的。
  3. 每個葉節點(NIL)是黑色的
  4. 如果一個節點是紅色的,則它的兩個子節點都是黑色的
  5. 對每個節點,從該節點到其所有後代葉節點的簡單路徑上,均包含相同數目的黑色節點

說明:

NIL< 是爲了方便處理紅黑樹代碼中的邊界條件,而設立的哨兵節點。

紅黑樹如下圖所示:
這裏寫圖片描述

節點類型設計

/**
 * 紅黑樹節點類
 */
class RedBlackTreeNode {
    //數據
    private RedBlackTreeNode parent;
    private RedBlackTreeNode left;
    private RedBlackTreeNode right;
    private int data;
    private TreeColor color;

    public RedBlackTreeNode() {
    }

    //這裏應該是一堆set,get方法
}
/**
 * 樹的顏色枚舉類
 */
enum TreeColor {
    Red("red"), Black("black");

    TreeColor(String string) {
    }
}

哨兵節點是一個與樹中普通節點具有相同屬性的對象,它的color屬性爲Black,其他屬性可以設置爲任意值。

黑高:

從某一個節點x出發(不包含該節點)到達一個葉節點的任意一條簡單路徑上的黑色節點的個數稱爲該節點的黑高。
紅黑樹的黑度爲其根節點的黑高。


下面來說說怎麼操作紅黑樹


紅黑樹的操作比較難的地方在於插入元素和刪除元素,這也是紅黑樹相對於二叉查找樹不同的地方。要想講清楚這個,必須先說紅黑樹的旋轉操作。

旋轉:

指針結構的修改是通過旋轉來完成的,這是一種能保持二叉搜索樹性質的搜索樹局部操作。旋轉就是怎麼斷開指針,和續上指針的過程。旋轉分爲左旋和右旋。

  • 左旋:
  • 右旋:
    如下圖所示:

代碼:

 /**
     * 將x節點向左旋轉,並不會改變節點值的數據大小關係,
     * 左旋和右旋很簡單,不需要考慮顏色的變化,
     * 只是將節點斷開,旋轉,再連上。知道斷開和連接的先後順序就比較好理解了。
     *
     * @param x 需要旋轉的節點
     */
    public void left_rotate(RedBlackTreeNode x) {
        //記錄x的右孩子節點
        RedBlackTreeNode y = x.getRight();
        //設置x的右孩子爲y的左孩子
        x.setRight(y.getLeft());
        //如果y的左孩子不爲哨兵節點,設置y左孩子的父親爲x
        if (y.getLeft() != NIL) {
            y.getLeft().setParent(x);
        }
        //設置y的父親爲x的父親
        y.setParent(x.getParent());

        if (x.getParent() == NIL) {
            //如果x的父親爲哨兵節點,表明x是根節點,那麼將y設置爲根節點就好了
            this.setRoot(y);
        } else if (x == x.getParent().getLeft()) {
            //如果x是x父親的左孩子,那麼設置x父親的左孩子爲y
            x.getParent().setLeft(y);
        } else {
            //如果x是x父親的右孩子,那麼設置x父親的右孩子爲y
            x.getParent().setRight(y);
        }
        //設置y的左孩子爲x
        y.setLeft(x);
        //設置x的父親爲y
        x.setParent(y);
    }

    /**
     * 將x節點向右旋轉,並不會改變節點值的數據大小關係,
     * 左旋和右旋很簡單,不需要考慮顏色的變化,
     * 只是將節點斷開,旋轉,再連上。知道斷開和連接的先後順序就比較好理解了。
     *
     * @param x 需要旋轉的節點
     */
    public void right_rotate(RedBlackTreeNode x) {
        //記錄x的左孩子節點
        RedBlackTreeNode y = x.getLeft();
        //設置x的左孩子爲y的右孩子
        x.setLeft(y.getRight());
        //如果y的右孩子不爲哨兵節點,設置y右孩子的父親爲x
        if (y.getRight() != NIL) {
            y.getRight().setParent(x);
        }
        //設置y的父親爲x的父親
        y.setParent(x.getParent());

        if (x.getParent() == NIL) {
            //如果x的父親爲哨兵節點,表明x是根節點,那麼將y設置爲根節點就好了
            this.setRoot(y);
        } else if (x == x.getParent().getLeft()) {
            //如果x是x父親的左孩子,那麼設置x父親的左孩子爲y
            x.getParent().setLeft(y);
        } else {
            //如果x是x父親的右孩子,那麼設置x父親的右孩子爲y
            x.getParent().setRight(y);
        }
        //設置y的右孩子爲x
        y.setRight(x);
        //設置x的父親爲y
        x.setParent(y);
    }

分析:

左旋右旋就是把需要旋轉的節點的左右孩子提升一層,然後斷開相應的指針,在續上相應的指針保持二叉搜索樹的性質。這裏的旋轉和二叉搜索樹基本上一致

旋轉說完了,咋們來說插入。

插入:

當然是必備的操作啦,想想看,原來的二叉搜索樹的插入過程只需要找到插入位置,判斷是插入到左邊還是右邊就行了,那麼可能不行了,因爲插入完了之後,會破壞紅黑樹的性質啊,那麼怎麼邊插入邊使紅黑樹的性質得到保持呢。這就需要插入的過程中修補(fix-up)紅黑樹的性質。


那麼什麼情況需要修補呢?假設 z 是剛插入的節點。


當第一次插入的時候,z必然是根節點,但是z的父親是NIL節點是黑色,所以爲了保持性質2,需要在某個地方將根節點設置爲Black。當然在插入的過程中也可能出現根節點變爲紅色的情況,那麼無論如何將根節點設置爲Black就行了。

因爲剛插入的節點 z 的默認設置是Red, 而當 z 的父親是紅色的時候破壞了性質4,需要修補,如果z 的父親不是紅色,沒有破壞任何性質。有了這個前提條件接下來再看。

在整個過程中只有性質2,4可能被破壞


  • z 的叔叔節點是z 的爺爺的右孩子

    情況1:z 的叔叔節點 y 是紅色的
    情況2:z 的叔叔節點 y 是黑色的,且 z 是一個右孩子
    情況3:z 的叔叔節點 y 是黑色的,且 z 是一個左孩子


  • z 的叔叔節點是 z 的爺爺的左孩子

    情況4:z 的叔叔節點 y 是紅色的
    情況5:z 的叔叔節點 y 是黑色的,且 z 是一個右孩子
    情況6:z 的叔叔節點 y 是黑色的,且 z 是一個左孩子


1,2,3和4,5,6是對稱的,如下圖所示,看圖和上面的說明仔細思考一下吧
這裏寫圖片描述

插入:

 /**
     * 插入函數,用來構造一個插入節點。
     *
     * @param k 插入的數據值
     */
    public void insert(int k) {
        RedBlackTreeNode node = new RedBlackTreeNode();
        node.setData(k);
        node.setColor(TreeColor.Red);
        //將需要插入的節點的所有元素都設置成哨兵節點
        node.setParent(NIL);
        node.setLeft(NIL);
        node.setRight(NIL);
        //調用紅黑二叉樹的插入方法
        rb_insert(node);
    }

    /**
     * 用於紅黑二叉樹的插入方法
     *
     * @param z 插入的節點對象
     */
    public void rb_insert(RedBlackTreeNode z) {
        //y是待插入的節點位置,默認也是哨兵節點
        RedBlackTreeNode y = this.NIL;
        //x是,根節點,不斷比較數據值,直到直到帶插入的位置
        RedBlackTreeNode x = this.getRoot();
        while (x != this.NIL) {
            y = x;
            if (z.getData() < x.getData()) {
                x = x.getLeft();
            } else {
                x = x.getRight();
            }
        }
        //將z插入到y後面
        z.setParent(y);
        if (y == NIL) {
            //如果z是第一個插入的值,將z設置爲根節點
            this.setRoot(z);
        } else if (z.getData() < y.getData()) {
            //將z插入到y的左邊
            y.setLeft(z);
        } else {
            //z插入到y的右邊
            y.setRight(z);
        }
        //插入節點可能破壞了紅黑樹性質,所以調用修補方法
        rb_insert_fixup(z);
        count++;
        System.out.println("insert:" + z.getData());
    }

    /**
     * 紅黑樹插入修補方法,主要有三種情況需要修補
     * 情況1:z的叔叔節點y是紅色的
     * 情況2:z的叔叔節點y是黑色的,且z是一個右孩子
     * 情況3:z的叔叔節點y是黑色的,且z是一個左孩子
     *
     * @param z 引起了性質變化的節點z
     */
    public void rb_insert_fixup(RedBlackTreeNode z) {
        //while循環是說z 的父親的顏色如果是紅色,就需要進行修補。
        // 第一次插入元素的時候,z的父親是哨兵節點,所以不用修補,直接執行while後面的設置根節點爲黑色的語句
        // 之後每次插入都需要判斷z節點的父親節點的顏色進行判斷,因爲插入的z節點初始設置爲紅色的,如果z的父親節點爲黑色,就不用修補了,如果爲紅色,那麼z的父節點和z都是紅色,那麼就需要進行修補
        while (z.getParent().getColor() == TreeColor.Red) {
            //如果z的父親 是z的爺爺的左孩子,這裏當z是插入的是第二節點的時候,z的父親是根節點,z的父親的父親是一個哨兵節點,所以走了else區域
            if (z.getParent() == z.getParent().getParent().getLeft()) {
                //得到z的叔叔,叔叔是z的爺爺的右孩子,所有的叔叔都有可能是哨兵節點,如果是哨兵節點的話,那麼叔叔的顏色就是黑色
                RedBlackTreeNode y = z.getParent().getParent().getRight();
                //判斷z的叔叔的顏色,如果叔叔是紅色,那麼z的父親一定是紅色,又因爲剛插入的z是紅色,所以違背了性質,
                if (y.getColor() == TreeColor.Red) {
                    //情況1:z的叔叔節點y是紅色的
                    //修改父親的顏色爲黑色
                    z.getParent().setColor(TreeColor.Black);
                    //叔叔的顏色也設置爲黑色
                    y.setColor(TreeColor.Black);
                    //z的爺爺的顏色設置爲紅色,這樣在z的爺爺爲跟的子樹上保持了性質
                    z.getParent().getParent().setColor(TreeColor.Red);
                    //開始從葉子節點往上走,可能z 的爺爺那麼一輩的性質被破壞了,所以再次循環,去看z的爺爺的父親的顏色是什麼
                    z = z.getParent().getParent();
                } else {
                    //表明z的叔叔的顏色,是黑色,此時z的父親的顏色不確定,

                    //如果z是z的父親的右孩子
                    if (z == z.getParent().getRight()) {
                        //情況2:z的叔叔節點y是黑色的,且z是一個右孩子,將z的父親左旋,保持性質
                        z = z.getParent();
                        left_rotate(z);
                    }
                    //情況3:z的叔叔節點y是黑色的,且z是一個左孩子

                    //將z的父親的顏色設置爲黑色
                    z.getParent().setColor(TreeColor.Black);
                    //將z的爺爺的顏色設置爲紅色
                    z.getParent().getParent().setColor(TreeColor.Red);
                    //將z的爺爺右旋
                    right_rotate(z.getParent().getParent());

                    //情況2後面緊跟情況3 ,是因爲情況2並沒有對顏色進行修改,只是調整了大小關係,通過情況3對顏色的修改,和旋轉操作,才能達到平衡


                }
            } else {
                //類似於上面
                //如果z的父親 是z的爺爺的右孩子
                //得到z的叔叔,叔叔是z爺爺的左孩子
                RedBlackTreeNode y = z.getParent().getParent().getLeft();
                //判斷z的叔叔的顏色,如果叔叔是紅色,那麼z的父親一定是紅色,又因爲剛插入的z是紅色,所以違背了性質,
                if (y.getColor() == TreeColor.Red) {
                    //情況1:z的叔叔節點y是紅色的
                    //修改父親的顏色爲黑色
                    z.getParent().setColor(TreeColor.Black);
                    //叔叔的顏色也設置爲黑色
                    y.setColor(TreeColor.Black);
                    //z的爺爺的顏色設置爲紅色,這樣在z的爺爺爲跟的子樹上保持了性質
                    z.getParent().getParent().setColor(TreeColor.Red);
                    //開始從葉子節點往上走,可能z 的爺爺那麼一輩的性質被破壞了,所以再次循環,去看z的爺爺的父親的顏色是什麼
                    z = z.getParent().getParent();
                } else {
                    //表明z的叔叔的顏色,是黑色,此時z的父親的顏色不確定,

                    //如果z是z的父親的右孩子
                    if (z == z.getParent().getLeft()) {
                        //情況2:z的叔叔節點y是黑色的,且z是一個左孩子,將z的父親右旋,保持性質
                        z = z.getParent();
                        right_rotate(z);
                    }
                    //情況3:z的叔叔節點y是黑色的,且z是一個右孩子
                    z.getParent().setColor(TreeColor.Black);
                    //將z的爺爺的顏色設置爲紅色
                    z.getParent().getParent().setColor(TreeColor.Red);
                    //將z的爺爺右旋
                    left_rotate(z.getParent().getParent());
                }
            }
        }
        //不管怎麼樣,將根節點設置爲黑色
        this.getRoot().setColor(TreeColor.Black);
    }

分析:

僅當情況1或者情況4發生,然後指針沿着樹上升2層,while循環纔會重複執行。此外,改程序所做的旋轉不超過兩次,因爲只要執行了情況2或情況3,或者5,6 ,while循環就結束了。

接下來,咋們說刪除。刪除真的是最難的一部分。

咋們先說怎麼將一個節點移到另外一個節點位置上,也是一個斷開指針和續上指針的過程,很簡單的:
代碼:

/**
     * 將一個元素移動到另外一個元素的位置上,用於刪除操作過程
     * 這個過程斷開了原來的deleted節點和父親的關係,將replace和deleted的父親設置成了父子關係
     * @param root 樹根節點
     * @param deleted 被刪除的元素
     * @param replace 用來移動到被刪除元素位置上的元素
     */
    public void rb_transplant(RedBlackTreeNode root,RedBlackTreeNode deleted,RedBlackTreeNode replace){

        if (deleted.getParent() == NIL){
            //被刪除的節點是根節點,那麼直接用replace節點來作爲根節點
            this.setRoot(replace);
        }else if (deleted == deleted.getParent().getLeft()){
            //被刪除的節點是它父親的左孩子,將replace設置爲他父親的左孩子
            deleted.getParent().setLeft(replace);
        }else {
            //被刪除的節點是它父親的右孩子,將replace設置爲他父親的右孩子
            deleted.getParent().setRight(replace);
        }
        //不管怎麼樣,肯定是要把replace的父親設置成deleted的父親的
        replace.setParent(deleted.getParent());
    }

那麼什麼情況下需要刪除,怎麼刪除,什麼情況下需要修補因爲刪除破壞了的紅黑樹性質,怎麼調整?

刪除操作分爲3種情況:

1、待刪除結點沒有子結點,即它是一個葉子結點,此時直接刪除

2、待刪除結點只有一個子結點,則可以直接刪除;如果待刪除結點是根結點,則它的子結點變爲根結點;如果待刪除結點不是根結點,則用它的子結點替代它的位置。

3、待刪除結點有兩個子結點,首先找出該結點的後繼結點(即右子樹中數值最小的那個結點),然後將兩個結點進行值交換(即:只交換兩個結點的數值,不改變結點的顏色)並將待刪除結點刪除,由於後繼結點不可能有左子結點,對調後的待刪除結點也不會有左子結點,因此要把它刪除的操作會落入情況(1)或情況(2)中。

上面的三種刪除情況和二叉搜索數是一樣的,但是在這個過程中需要記錄 y 的原始的顏色,和 y的孩子 x。刪除完了,如果 y 的原始顏色是黑色就需要修補紅黑樹的性質。

代碼:

二、.紅黑樹的刪除結點算法

1.待刪除結點有兩個孩子結點,操作如下:

(1)直接把該結點調整爲葉結點(對應上面刪除操作的,情況3)

(2)若該結點是紅色,則可直接刪除,不影響紅黑樹的性質,算法結束

(3)若該結點是黑色,則刪除後紅黑樹不平衡。此時要進行“雙黑”操作

  記該結點爲V,則刪除了V後,從根結點到V的所有子孫葉結點的路徑將會比樹中其他的從根結點到葉結點的路徑擁有更少的黑色結點, 破壞了紅黑樹性質4。此時,用“雙黑”結點來表示從根結點到這個“雙黑”結點的所有子孫葉結點的路徑上都缺少一個黑色結點。  

雙黑含義:該結點需要代表兩個黑色結點,才能維持樹的平衡

這裏寫圖片描述

如上圖所示,要刪除結點90,則刪除後從根結點到結點90的所有子樹結點的路徑上的黑色結點比從根點到葉結點的路徑上的黑結點少。因而,刪除結點90後,用子結點NULL代替90結點,並置爲“雙黑”結點。

2 . 待刪除結點有一個孩子結點,操作爲:
該節點是黑色,其非空子節點爲紅色 ;則將其子節點提升到該結點位置,顏色變黑

3.“雙黑”結點的處理

分四種情況:

(1)雙黑結點 x 的兄弟結點 w 是紅色結點
(2)雙黑結點 x 的兄弟結點 w 是黑色,且有兩個黑色子結點
(3)雙黑色結點 x 的兄弟結點 w 是黑色,且w的左孩子是紅色的,右孩子是黑色的
(4)雙黑色結點 x 的兄弟結點 w 是黑色,且w的右孩子是紅色的

(1)的處理方法如下圖
這裏寫圖片描述

(2)的處理方法如下圖
這裏寫圖片描述

(3)的處理方法如下圖
這裏寫圖片描述

(4)的處理方法如下圖
這裏寫圖片描述

刪除操作代碼:

/**
     * 對外提供的刪除某一個元素之的函數,
     * @param k 需要刪除的元素值
     */
    public void delete(int k){
        //找到需要刪除的值對應的函數
        RedBlackTreeNode node = searchRecursion(this.getRoot(), k);
        //調用紅黑二叉樹的刪除操作
        rb_delete(this.getRoot(), node);
    }

    /**
     * 紅黑二叉樹的刪除操作,這個函數直觀刪除某一個元素,不管調整紅黑樹性質
     * @param root 紅黑樹的根節點
     * @param z 需要刪除的節點
     */
    public void rb_delete(RedBlackTreeNode root,RedBlackTreeNode z){
        //維持節點 y 爲 從樹中  刪除的節點  或者 需要   和刪除節點互換   的節點。
        RedBlackTreeNode y = z;
        //記錄下 y 的最開始的顏色
        TreeColor y_original_color = y.getColor();
        //聲明 x 節點爲 ,後面設置x 節點爲 y節點的右孩子,或者左孩子
        RedBlackTreeNode x = null;

        //z的左孩子爲空
        //如果z 只有一個右孩子節點,這種情況只可能是 z 是某一個葉子節點的上一層節點,
        if (z.getLeft() == NIL){
            //進入到這裏面說明z的右孩子可有可無。
            // 如果 z 有右孩子的話,那麼 z 一定是黑色的;;
            // 如果 z 沒有右孩子的話,那麼 z 可能是紅色的可能是黑色的,一定是葉子節點。

            //因爲如果z是紅色節點,如果z存在葉子節點的話,那麼必然同時存在2 個黑色葉子節點(性質3)
            // 如果 z 是黑色節點,如果 z 存在葉子節點的話,如果存在的右孩子爲紅色,則正常;   如果右孩子爲黑色(違反性質5)因爲沒有左孩子啊

            //記錄x 爲 z的右孩子節點,不管  z 有沒有右孩子,如果沒有右孩子,z就是 NIL 節點
            x = z.getRight();
            //將  z 和 z 的 右孩子互換,不管 z 的右孩子是什麼
            rb_transplant(root, z, z.getRight());
        }else if (z.getRight() == NIL){
            //進入到這裏面說明z的左孩子一定是有的,但是沒有右孩子。
            //這時候只有一種情況,就是 z 是黑色的,並且左孩子是紅色的。。。
            //因爲如果 z 是紅色節點,又因爲z 是有左孩子的,那麼z 必然有右孩子(否則違反性質4) ,不會進入到這裏的
            // 如果 z 是黑色節點,又因爲z 是有左孩子的,如果左孩子是黑色的,那麼 必然會有右孩子且是黑色的(否則違反性質5)。

            //記錄x 爲 z的左孩子節點,z 一定是有左孩子的
            x= z.getLeft();
            //將 z和z的左孩子互換
            rb_transplant(root, z, z.getLeft());
        }else {
            //進入到這裏,說明 z 既有左孩子,又有右孩子

            //記錄下 z 的後繼節點 y ,以後會將 y 和 z 互換
            y= minResursive(z.getRight());
            //保存下來  y 的原始顏色
            y_original_color=y.getColor();
            // 記錄下 y  的右孩子節點,x 要麼是一個NIL 節點或者一個真實的葉節點。
            // 因爲y 是某一顆子樹的最小值,那麼 y 必然是子樹最左邊的葉節點,或者葉節點的上一層節點。
            //因爲 如果y 爲紅色,那麼y 必然是葉節點,因爲如果 y不是葉節點,那麼 y 必然帶有黑色的兩個子節點(性質4),那麼y 就不會是子樹最小值
            //如果 y 爲黑色,那麼 y 可能是葉節點,也可能 y 有一個右孩子節點(不可能有左孩子節點,不然y就是不是最小值了),這個右孩子節點一定是紅色,(如果不是紅色,就違反了性質5)
            x = y.getRight();
            if (y.getParent() == z){
                //如果 y 的父親是 z,也就是說,z的後繼就是 z 的孩子節點,,這時候 y 必然沒有孩子節點,這裏面的 x 是NIL節點
                //將x 的父親設置爲 y,,似乎可以省略這裏,待會兒再試
                x.setParent(y);
            }else {
                //y 不是z 的右孩子,那麼先將 y 和y 的右孩子互換,
                rb_transplant(root,y,y.getRight());
                //將y的右孩子設置成 z的右孩子
                y.setRight(z.getRight());
                //將y的右孩子和 y 續上關係
                y.getRight().setParent(y);
            }
            //接下來,將y 與 z 互換
            rb_transplant(root,z,y);
            //將 y 和 z 左邊部分續上關係
            y.setLeft(z.getLeft());
            y.getLeft().setParent(y);
            //將 y 的顏色設置爲 z 的顏色
            y.setColor(z.getColor());
        }

        //如果 y 的顏色原來是黑色,那麼可能破壞了性質,需要修補
        //因爲如果 y 的顏色如果是紅色,那麼 將 y 和 z 互換位置,不會破壞關係。
        // 因爲上面 y.setColor(z.getColor()); 將y的顏色設置爲了以前 z的顏色,保持了性質1,2,3,4 ,接着y 又是紅色,將紅色的節點刪除不會破壞性質5,
        if (y_original_color == TreeColor.Black){
            //這裏的 x 要麼是一個 葉節點,要麼是一個NIL節點。
            //爲什麼需要針對 x 進行修補? 因爲可以看做 x 最後替換了 y的位置。(參考x 的賦值語句),
            //將現在佔有y 原來位置的節點 x 視爲還有一重 額外的黑色。
            rb_delete_fixup(root,x);
        }

    }
    /**
     * 修補因爲刪除了節點破壞了的紅黑樹性質
     * @param root 紅黑樹的根
     * @param x 需要修補的節點
     */
    public void rb_delete_fixup(RedBlackTreeNode root,RedBlackTreeNode x){

        while (x != root && x.getColor() == TreeColor.Black){
            if (x == x.getParent().getLeft()){
                RedBlackTreeNode w = x.getParent().getRight();
                if (w.getColor() == TreeColor.Red){
                    w.setColor( TreeColor.Black);
                    x.getParent().setColor(TreeColor.Red);
                    left_rotate(x.getParent());
                    w =x.getParent().getRight();
                }
                if (w.getLeft().getColor() == TreeColor.Black && w.getRight().getColor() ==TreeColor.Black){
                    w.setColor(TreeColor.Red);
                    x=x.getParent();
                }else {
                    if (w.getRight().getColor() == TreeColor.Black) {
                        w.getLeft().setColor(TreeColor.Black);
                        w.setColor(TreeColor.Red);
                        right_rotate(w);
                        w = x.getParent().getRight();
                    }
                    w.setColor( x.getParent().getColor());
                    x.getParent().setColor(TreeColor.Black);
                    w.getRight().setColor(TreeColor.Black);
                    left_rotate(x.getParent());
                    x=getRoot();
                }
            }else {
                RedBlackTreeNode w = x.getParent().getLeft();
                if (w.getColor() == TreeColor.Red){
                    w.setColor(TreeColor.Black);
                    x.getParent().setColor(TreeColor.Red);
                    right_rotate(x.getParent());
                    w =x.getParent().getLeft();
                }
                if (w.getRight().getColor() == TreeColor.Black && w.getLeft().getColor() ==TreeColor.Black){
                    w.setColor(TreeColor.Red);
                    x=x.getParent();
                }else {
                    if (w.getLeft().getColor() == TreeColor.Black) {
                        w.getRight().setColor(TreeColor.Black);
                        w.setColor(TreeColor.Red);
                        left_rotate(w);
                        w = x.getParent().getLeft();
                    }
                    w.setColor(x.getParent().getColor());
                    x.getParent().setColor(TreeColor.Black);
                    w.getLeft().setColor(TreeColor.Black);
                    right_rotate(x.getParent());
                    x=getRoot();
                }
            }
        }
        x.setColor(TreeColor.Black);
    }

引理:一顆有n個內部節點的紅黑樹的高度至多爲:2lg(n+1)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章