紅黑樹

紅黑樹原理

首先說在前面的一點,平衡二叉樹的查找性能比紅黑樹要高,但卻沒有紅黑樹用的普遍。主要是因為平衡二叉樹追求絕對平衡,導(dǎo)致插入時出現(xiàn)不平衡就要旋轉(zhuǎn),而旋轉(zhuǎn)的代價太大。紅黑樹只是追求大致平衡,但是插入時調(diào)整的效率更高,因而普遍使用。

紅黑樹(Red-Black Tree,簡稱R-B Tree),它是一種特殊的二叉查找樹。首先它滿足二叉查找樹的特征:任意結(jié)點結(jié)點包含的鍵值,大于左孩子的鍵值,小于右孩子的鍵值。
除此之外,紅黑樹的每個結(jié)點都有存儲位來表示結(jié)點的顏色,不是紅(Red)就是 黑(Black)。
紅黑樹的特性:

  • 1.每個結(jié)點或是紅色的,或是黑色的。
  • 2.根結(jié)點是黑色的。
  • 3.每個葉結(jié)點(NIL)是黑色的。(最后的葉結(jié)點就是空的,或者用一個哨兵替換掉所有的空結(jié)點)
  • 4.如果一個結(jié)點是紅色的,那么它的兩個子結(jié)點都是黑色的。
  • 5.對每個結(jié)點,從該結(jié)點到其所有后代葉結(jié)點的簡單路徑上,均包含相同數(shù)目的黑色結(jié)點。

關(guān)于它的特性,需要注意:
第一,特性 3 中的葉子結(jié)點,是為空(NIL或null)的結(jié)點
第二,特性 5 ,確保沒有一條路徑會比其他路徑長出2倍,因而是近似平衡的。

紅黑樹的圖如下(直接借用算法導(dǎo)論中的圖,其中淺色的為紅色,深色的為黑色):

紅黑樹的java代碼實現(xiàn)

紅黑樹的基本操作是查找,選擇,添加,刪除。查找就省略了,和二叉樹的查找區(qū)別不大。在添加和刪除后,都會用到紅黑樹的旋轉(zhuǎn)。因為修改紅黑樹之后,會破壞紅黑樹的性質(zhì),所以需要旋轉(zhuǎn)滿足這幾條性質(zhì)。
旋轉(zhuǎn)包括兩種:左旋右旋

1.基本定義

public class RBTree<T extends Comparable<T>>{
    
    private RBTNode<T> mRoot;  //根結(jié)點
    
    private static final boolean RED = false;
    private static final boolean BLACK = true;
    
    public class RBTNode<T extends Comparable<T>>{
        boolean color;  //顏色
        T key; //關(guān)鍵字(鍵值)
        RBTNode<T> left; //左孩子
        RBTNode<T> right; //右孩子
        RBTNode<T> parent; //父節(jié)點
        
        public RBTNode(T key, boolean color, RBTNode<T> parent, RBTNode<T> left, RBTNode<T> right) {
            this.key = key;
            this.color = color;
            this.parent = parent;
            this.left = left;
            this.right = right;
        }
    }

        ...
}

說明:RBTree是紅黑樹對應(yīng)的類,RBTNode是紅黑樹的結(jié)點類。在RBTree中包含了根節(jié)點mRoot和紅黑樹的相關(guān)API。

2.左旋

對x進(jìn)行左旋,意味著將x變成一個左結(jié)點。

左旋代碼

/* 
     * 對紅黑樹的節(jié)點(x)進(jìn)行左旋轉(zhuǎn)
     *
     * 左旋示意圖(對節(jié)點x進(jìn)行左旋):
     *      px                              px
     *     /                               /
     *    x                               y                
     *   /  \      --(左旋)-.           / \                #
     *  lx   y                          x  ry     
     *     /   \                       /  \
     *    ly   ry                     lx  ly  
     *
     *
     */
    private void leftRotate(RBTNode<T> x){
        //設(shè)置x的右孩子為y
        RBTNode<T> y = x.right;
        
        //將 "y的左孩子" 設(shè)為 "x的右孩子"
        //如果y的左孩子非空,將 "x" 設(shè)為 "y的左孩子的父親"
        x.right = y.left;
        if(y.left != null){
            y.left.parent = x;
        }
        
        //將 "x的父親" 設(shè)為 "y的父親"
        y.parent = x.parent;
        
        if(x.parent == null){
            this.mRoot = y;          //如果 "x的父親" 是空結(jié)點,則將y設(shè)為根結(jié)點
        }else{
            if(x.parent.left == x){
                x.parent.left = y;   //如果 x是它父節(jié)點的左孩子,則將y設(shè)置為 "x的父節(jié)點的左孩子"
            }else{
                x.parent.right = y;  //如果 x是它父節(jié)點的右孩子,則將y設(shè)置為 "x的父節(jié)點的右孩子"
            }
        }
        
        // 將 “x” 設(shè)為 “y的左孩子”
        y.left = x;
        // 將 “x的父節(jié)點” 設(shè)為 “y”
        x.parent = y;
    }

3.右旋

對y進(jìn)行左旋,意味著"將y變成一個右節(jié)點"。

右旋代碼:

/* 
 * 對紅黑樹的節(jié)點(y)進(jìn)行右旋轉(zhuǎn)
 *
 * 右旋示意圖(對節(jié)點y進(jìn)行左旋):
 *            py                               py
 *           /                                /
 *          y                                x                  
 *         /  \      --(右旋)-.            /  \                     #
 *        x   ry                           lx   y  
 *       / \                                   / \                   #
 *      lx  rx                                rx  ry
 * 
 */
private void rightRotate(RBTNode<T> y) {
    // 設(shè)置x是當(dāng)前節(jié)點的左孩子。
    RBTNode<T> x = y.left;

    // 將 “x的右孩子” 設(shè)為 “y的左孩子”;
    // 如果"x的右孩子"不為空的話,將 “y” 設(shè)為 “x的右孩子的父親”
    y.left = x.right;
    if (x.right != null)
        x.right.parent = y;

    // 將 “y的父親” 設(shè)為 “x的父親”
    x.parent = y.parent;

    if (y.parent == null) {
        this.mRoot = x;            // 如果 “y的父親” 是空節(jié)點,則將x設(shè)為根節(jié)點
    } else {
        if (y == y.parent.right)
            y.parent.right = x;    // 如果 y是它父節(jié)點的右孩子,則將x設(shè)為“y的父節(jié)點的右孩子”
        else
            y.parent.left = x;    // (y是它父節(jié)點的左孩子) 將x設(shè)為“x的父節(jié)點的左孩子”
    }

    // 將 “y” 設(shè)為 “x的右孩子”
    x.right = y;

    // 將 “y的父節(jié)點” 設(shè)為 “x”
    y.parent = x;
}

4.添加

將結(jié)點插入紅黑樹時,首先將紅黑樹當(dāng)作一棵二叉查找樹,然后將結(jié)點插入;然后將結(jié)點著色為紅色,再通過“旋轉(zhuǎn)和重新著色”等操作修正該樹,使其成為一棵紅黑樹。詳細(xì)步驟如下:
第一步:將紅黑樹當(dāng)作一棵二叉查找樹,將結(jié)點插入(這個簡單,不展開)。

第二步:將插入的結(jié)點著色為“紅色”
為什么著色為紅色,而不是黑色?可以觀測紅黑樹的5個特性,發(fā)現(xiàn)不會違背“特性(5)”,當(dāng)然有可能違背其他特性,但只是可能,不一定會,所以遵循著違背的越少越好的原則,將插入的結(jié)點著色為紅色。

第三步:通過一系列的選擇或者著色等操作,使其重新成為紅黑樹
第二步中,將插入節(jié)點著色為"紅色"之后,不會違背"特性(5)"。那它到底會違背哪些特性呢?
對于"特性(1)",顯然不會違背了。因為我們已經(jīng)將它涂成紅色了。
對于"特性(2)",顯然也不會違背。在第一步中,我們是將紅黑樹當(dāng)作二叉查找樹,然后執(zhí)行的插入操作。而根據(jù)二叉查找數(shù)的特點,插入操作不會改變根節(jié)點。所以,根節(jié)點仍然是黑色。
對于"特性(3)",顯然不會違背了。這里的葉子節(jié)點是指的空葉子節(jié)點,插入非空節(jié)點并不會對它們造成影響。
對于"特性(4)",是有可能違背的!
那接下來,想辦法使之"滿足特性(4)",就可以將樹重新構(gòu)造成紅黑樹了。

添加操作的實現(xiàn)代碼

/* 
 * 將結(jié)點插入到紅黑樹中
 *
 * 參數(shù)說明:
 *     node 插入的結(jié)點        // 對應(yīng)《算法導(dǎo)論》中的node
 */
private void insert(RBTNode<T> node) {
    int cmp;
    RBTNode<T> y = null;
    RBTNode<T> x = this.mRoot;

    // 1. 將紅黑樹當(dāng)作一顆二叉查找樹,將節(jié)點添加到二叉查找樹中。
    while (x != null) {
        y = x;
        cmp = node.key.compareTo(x.key);
        if (cmp < 0)
            x = x.left;
        else
            x = x.right;
    }

    node.parent = y;
    if (y!=null) {
        cmp = node.key.compareTo(y.key);
        if (cmp < 0)
            y.left = node;
        else
            y.right = node;
    } else {
        this.mRoot = node;
    }

    // 2. 設(shè)置節(jié)點的顏色為紅色
    node.color = RED;

    // 3. 將它重新修正為一顆二叉查找樹
    insertFixUp(node);
}

/* 
 * 新建結(jié)點(key),并將其插入到紅黑樹中
 *
 * 參數(shù)說明:
 *     key 插入結(jié)點的鍵值
 */
public void insert(T key) {
    RBTNode<T> node=new RBTNode<T>(key,BLACK,null,null,null);

    // 如果新建結(jié)點失敗,則返回。
    if (node != null)
        insert(node);
}

內(nèi)部接口 -- insert(node)的作用是將"node"節(jié)點插入到紅黑樹中。
外部接口 -- insert(key)的作用是將"key"添加到紅黑樹中。

添加修正操作的實現(xiàn)代碼

/*
 * 紅黑樹插入修正函數(shù)
 *
 * 在向紅黑樹中插入節(jié)點之后(失去平衡),再調(diào)用該函數(shù);
 * 目的是將它重新塑造成一顆紅黑樹。
 *
 * 參數(shù)說明:
 *     node 插入的結(jié)點        // 對應(yīng)《算法導(dǎo)論》中的z
 */
private void insertFixUp(RBTNode<T> node) {
    RBTNode<T> parent, gparent;

    // 若“父節(jié)點存在,并且父節(jié)點的顏色是紅色”
    while (((parent = parentOf(node))!=null) && isRed(parent)) {
        gparent = parentOf(parent);

        //若“父節(jié)點”是“祖父節(jié)點的左孩子”
        if (parent == gparent.left) {
            // Case 1條件:叔叔節(jié)點是紅色
            RBTNode<T> uncle = gparent.right;
            if ((uncle!=null) && isRed(uncle)) {
                setBlack(uncle);
                setBlack(parent);
                setRed(gparent);
                node = gparent;
                continue;
            }

            // Case 2條件:叔叔是黑色,且當(dāng)前節(jié)點是右孩子
            if (parent.right == node) {
                RBTNode<T> tmp;
                leftRotate(parent);
                tmp = parent;
                parent = node;
                node = tmp;
            }

            // Case 3條件:叔叔是黑色,且當(dāng)前節(jié)點是左孩子。
            setBlack(parent);
            setRed(gparent);
            rightRotate(gparent);
        } else {    //若“z的父節(jié)點”是“z的祖父節(jié)點的右孩子”
            // Case 1條件:叔叔節(jié)點是紅色
            RBTNode<T> uncle = gparent.left;
            if ((uncle!=null) && isRed(uncle)) {
                setBlack(uncle);
                setBlack(parent);
                setRed(gparent);
                node = gparent;
                continue;
            }

            // Case 2條件:叔叔是黑色,且當(dāng)前節(jié)點是左孩子
            if (parent.left == node) {
                RBTNode<T> tmp;
                rightRotate(parent);
                tmp = parent;
                parent = node;
                node = tmp;
            }

            // Case 3條件:叔叔是黑色,且當(dāng)前節(jié)點是右孩子。
            setBlack(parent);
            setRed(gparent);
            leftRotate(gparent);
        }
    }

    // 將根節(jié)點設(shè)為黑色
    setBlack(this.mRoot);
}

insertFixUp(node)的作用是對應(yīng)"上面所講的第三步"。它是一個內(nèi)部接口。

5.刪除操作

將紅黑樹內(nèi)的某一個節(jié)點刪除。需要執(zhí)行的操作依次是:首先,將紅黑樹當(dāng)作一顆二叉查找樹,將該節(jié)點從二叉查找樹中刪除;然后,通過"旋轉(zhuǎn)和重新著色"等一系列來修正該樹,使之重新成為一棵紅黑樹。詳細(xì)描述如下:
第一步:將紅黑樹當(dāng)作一棵二叉查找樹,將結(jié)點刪除
這和"刪除常規(guī)二叉查找樹中刪除節(jié)點的方法是一樣的"。分3種情況:
① 被刪除節(jié)點沒有兒子,即為葉節(jié)點。那么,直接將該節(jié)點刪除就OK了。
② 被刪除節(jié)點只有一個兒子。那么,直接刪除該節(jié)點,并用該節(jié)點的唯一子節(jié)點頂替它的位置。
③ 被刪除節(jié)點有兩個兒子。那么,先找出它的后繼節(jié)點;然后把“它的后繼節(jié)點的內(nèi)容”復(fù)制給“該節(jié)點的內(nèi)容”;之后,刪除“它的后繼節(jié)點”。在這里,后繼節(jié)點相當(dāng)于替身,在將后繼節(jié)點的內(nèi)容復(fù)制給"被刪除節(jié)點"之后,再將后繼節(jié)點刪除。這樣就巧妙的將問題轉(zhuǎn)換為"刪除后繼節(jié)點"的情況了,下面就考慮后繼節(jié)點。 在"被刪除節(jié)點"有兩個非空子節(jié)點的情況下,它的后繼節(jié)點不可能是雙子非空。既然"的后繼節(jié)點"不可能雙子都非空,就意味著"該節(jié)點的后繼節(jié)點"要么沒有兒子,要么只有一個兒子。若沒有兒子,則按"情況① "進(jìn)行處理;若只有一個兒子,則按"情況② "進(jìn)行處理。

第二步:通過“旋轉(zhuǎn)和重新著色”等一系列操作修正該樹,使其重新成為一棵紅黑樹。
因為"第一步"中刪除節(jié)點之后,可能會違背紅黑樹的特性。所以需要通過"旋轉(zhuǎn)和重新著色"來修正該樹,使之重新成為一棵紅黑樹。

/* 
 * 刪除結(jié)點(node),并返回被刪除的結(jié)點
 *
 * 參數(shù)說明:
 *     node 刪除的結(jié)點
 */
private void remove(RBTNode<T> node) {
    RBTNode<T> child, parent;
    boolean color;

    // 被刪除節(jié)點的"左右孩子都不為空"的情況。
    if ( (node.left!=null) && (node.right!=null) ) {
        // 被刪節(jié)點的后繼節(jié)點。(稱為"取代節(jié)點")
        // 用它來取代"被刪節(jié)點"的位置,然后再將"被刪節(jié)點"去掉。
        RBTNode<T> replace = node;

        // 獲取后繼節(jié)點
        replace = replace.right;
        while (replace.left != null)
            replace = replace.left;

        // "node節(jié)點"不是根節(jié)點(只有根節(jié)點不存在父節(jié)點)
        if (parentOf(node)!=null) {
            if (parentOf(node).left == node)
                parentOf(node).left = replace;
            else
                parentOf(node).right = replace;
        } else {
            // "node節(jié)點"是根節(jié)點,更新根節(jié)點。
            this.mRoot = replace;
        }

        // child是"取代節(jié)點"的右孩子,也是需要"調(diào)整的節(jié)點"。
        // "取代節(jié)點"肯定不存在左孩子!因為它是一個后繼節(jié)點。
        child = replace.right;
        parent = parentOf(replace);
        // 保存"取代節(jié)點"的顏色
        color = colorOf(replace);

        // "被刪除節(jié)點"是"它的后繼節(jié)點的父節(jié)點"
        if (parent == node) {
            parent = replace;
        } else {
            // child不為空
            if (child!=null)
                setParent(child, parent);
            parent.left = child;

            replace.right = node.right;
            setParent(node.right, replace);
        }

        replace.parent = node.parent;
        replace.color = node.color;
        replace.left = node.left;
        node.left.parent = replace;

        if (color == BLACK)
            removeFixUp(child, parent);

        node = null;
        return ;
    }

    if (node.left !=null) {
        child = node.left;
    } else {
        child = node.right;
    }

    parent = node.parent;
    // 保存"取代節(jié)點"的顏色
    color = node.color;

    if (child!=null)
        child.parent = parent;

    // "node節(jié)點"不是根節(jié)點
    if (parent!=null) {
        if (parent.left == node)
            parent.left = child;
        else
            parent.right = child;
    } else {
        this.mRoot = child;
    }

    if (color == BLACK)
        removeFixUp(child, parent);
    node = null;
}

/* 
 * 刪除結(jié)點(z),并返回被刪除的結(jié)點
 *
 * 參數(shù)說明:
 *     tree 紅黑樹的根結(jié)點
 *     z 刪除的結(jié)點
 */
public void remove(T key) {
    RBTNode<T> node; 

    if ((node = search(mRoot, key)) != null)
        remove(node);
}

內(nèi)部接口 -- remove(node)的作用是將"node"節(jié)點插入到紅黑樹中。
外部接口 -- remove(key)刪除紅黑樹中鍵值為key的節(jié)點。

刪除修正操作的實現(xiàn)代碼

/*
 * 紅黑樹刪除修正函數(shù)
 *
 * 在從紅黑樹中刪除插入節(jié)點之后(紅黑樹失去平衡),再調(diào)用該函數(shù);
 * 目的是將它重新塑造成一顆紅黑樹。
 *
 * 參數(shù)說明:
 *     node 待修正的節(jié)點
 */
private void removeFixUp(RBTNode<T> node, RBTNode<T> parent) {
    RBTNode<T> other;

    while ((node==null || isBlack(node)) && (node != this.mRoot)) {
        if (parent.left == node) {
            other = parent.right;
            if (isRed(other)) {
                // Case 1: x的兄弟w是紅色的  
                setBlack(other);
                setRed(parent);
                leftRotate(parent);
                other = parent.right;
            }

            if ((other.left==null || isBlack(other.left)) &&
                (other.right==null || isBlack(other.right))) {
                // Case 2: x的兄弟w是黑色,且w的倆個孩子也都是黑色的  
                setRed(other);
                node = parent;
                parent = parentOf(node);
            } else {

                if (other.right==null || isBlack(other.right)) {
                    // Case 3: x的兄弟w是黑色的,并且w的左孩子是紅色,右孩子為黑色。  
                    setBlack(other.left);
                    setRed(other);
                    rightRotate(other);
                    other = parent.right;
                }
                // Case 4: x的兄弟w是黑色的;并且w的右孩子是紅色的,左孩子任意顏色。
                setColor(other, colorOf(parent));
                setBlack(parent);
                setBlack(other.right);
                leftRotate(parent);
                node = this.mRoot;
                break;
            }
        } else {

            other = parent.left;
            if (isRed(other)) {
                // Case 1: x的兄弟w是紅色的  
                setBlack(other);
                setRed(parent);
                rightRotate(parent);
                other = parent.left;
            }

            if ((other.left==null || isBlack(other.left)) &&
                (other.right==null || isBlack(other.right))) {
                // Case 2: x的兄弟w是黑色,且w的倆個孩子也都是黑色的  
                setRed(other);
                node = parent;
                parent = parentOf(node);
            } else {

                if (other.left==null || isBlack(other.left)) {
                    // Case 3: x的兄弟w是黑色的,并且w的左孩子是紅色,右孩子為黑色。  
                    setBlack(other.right);
                    setRed(other);
                    leftRotate(other);
                    other = parent.left;
                }

                // Case 4: x的兄弟w是黑色的;并且w的右孩子是紅色的,左孩子任意顏色。
                setColor(other, colorOf(parent));
                setBlack(parent);
                setBlack(other.left);
                rightRotate(parent);
                node = this.mRoot;
                break;
            }
        }
    }

    if (node!=null)
        setBlack(node);
}

removeFixup(node, parent)是對應(yīng)"上面所講的第三步"。它是一個內(nèi)部接口。

紅黑樹的完整源碼

下面是紅黑樹實現(xiàn)的完整代碼和相應(yīng)的測試程序。
(1) 除了上面所說的"左旋"、"右旋"、"添加"、"刪除"等基本操作之后,還實現(xiàn)了"遍歷"、"查找"、"打印"、"最小值"、"最大值"、"創(chuàng)建"、"銷毀"等接口。
(2) 函數(shù)接口大多分為內(nèi)部接口和外部接口。內(nèi)部接口是private函數(shù),外部接口則是public函數(shù)。
(3) 測試代碼中提供了"插入"和"刪除"動作的檢測開關(guān)。默認(rèn)是關(guān)閉的,打開方法可以參考"代碼中的說明"。建議在打開開關(guān)后,在草稿上自己動手繪制一下紅黑樹。

紅黑樹的實現(xiàn)文件(RBTree.java)

/**
 * Java 語言: 紅黑樹
 *
 * @author skywang
 * @date 2013/11/07
 */

public class RBTree<T extends Comparable<T>> {

    private RBTNode<T> mRoot;    // 根結(jié)點

    private static final boolean RED   = false;
    private static final boolean BLACK = true;

    public class RBTNode<T extends Comparable<T>> {
        boolean color;        // 顏色
        T key;                // 關(guān)鍵字(鍵值)
        RBTNode<T> left;    // 左孩子
        RBTNode<T> right;    // 右孩子
        RBTNode<T> parent;    // 父結(jié)點

        public RBTNode(T key, boolean color, RBTNode<T> parent, RBTNode<T> left, RBTNode<T> right) {
            this.key = key;
            this.color = color;
            this.parent = parent;
            this.left = left;
            this.right = right;
        }

        public T getKey() {
            return key;
        }

        public String toString() {
            return ""+key+(this.color==RED?"(R)":"B");
        }
    }

    public RBTree() {
        mRoot=null;
    }

    private RBTNode<T> parentOf(RBTNode<T> node) {
        return node!=null ? node.parent : null;
    }
    private boolean colorOf(RBTNode<T> node) {
        return node!=null ? node.color : BLACK;
    }
    private boolean isRed(RBTNode<T> node) {
        return ((node!=null)&&(node.color==RED)) ? true : false;
    }
    private boolean isBlack(RBTNode<T> node) {
        return !isRed(node);
    }
    private void setBlack(RBTNode<T> node) {
        if (node!=null)
            node.color = BLACK;
    }
    private void setRed(RBTNode<T> node) {
        if (node!=null)
            node.color = RED;
    }
    private void setParent(RBTNode<T> node, RBTNode<T> parent) {
        if (node!=null)
            node.parent = parent;
    }
    private void setColor(RBTNode<T> node, boolean color) {
        if (node!=null)
            node.color = color;
    }

    /*
     * 前序遍歷"紅黑樹"
     */
    private void preOrder(RBTNode<T> tree) {
        if(tree != null) {
            System.out.print(tree.key+" ");
            preOrder(tree.left);
            preOrder(tree.right);
        }
    }

    public void preOrder() {
        preOrder(mRoot);
    }

    /*
     * 中序遍歷"紅黑樹"
     */
    private void inOrder(RBTNode<T> tree) {
        if(tree != null) {
            inOrder(tree.left);
            System.out.print(tree.key+" ");
            inOrder(tree.right);
        }
    }

    public void inOrder() {
        inOrder(mRoot);
    }


    /*
     * 后序遍歷"紅黑樹"
     */
    private void postOrder(RBTNode<T> tree) {
        if(tree != null)
        {
            postOrder(tree.left);
            postOrder(tree.right);
            System.out.print(tree.key+" ");
        }
    }

    public void postOrder() {
        postOrder(mRoot);
    }


    /*
     * (遞歸實現(xiàn))查找"紅黑樹x"中鍵值為key的節(jié)點
     */
    private RBTNode<T> search(RBTNode<T> x, T key) {
        if (x==null)
            return x;

        int cmp = key.compareTo(x.key);
        if (cmp < 0)
            return search(x.left, key);
        else if (cmp > 0)
            return search(x.right, key);
        else
            return x;
    }

    public RBTNode<T> search(T key) {
        return search(mRoot, key);
    }

    /*
     * (非遞歸實現(xiàn))查找"紅黑樹x"中鍵值為key的節(jié)點
     */
    private RBTNode<T> iterativeSearch(RBTNode<T> x, T key) {
        while (x!=null) {
            int cmp = key.compareTo(x.key);

            if (cmp < 0) 
                x = x.left;
            else if (cmp > 0) 
                x = x.right;
            else
                return x;
        }

        return x;
    }

    public RBTNode<T> iterativeSearch(T key) {
        return iterativeSearch(mRoot, key);
    }

    /* 
     * 查找最小結(jié)點:返回tree為根結(jié)點的紅黑樹的最小結(jié)點。
     */
    private RBTNode<T> minimum(RBTNode<T> tree) {
        if (tree == null)
            return null;

        while(tree.left != null)
            tree = tree.left;
        return tree;
    }

    public T minimum() {
        RBTNode<T> p = minimum(mRoot);
        if (p != null)
            return p.key;

        return null;
    }
     
    /* 
     * 查找最大結(jié)點:返回tree為根結(jié)點的紅黑樹的最大結(jié)點。
     */
    private RBTNode<T> maximum(RBTNode<T> tree) {
        if (tree == null)
            return null;

        while(tree.right != null)
            tree = tree.right;
        return tree;
    }

    public T maximum() {
        RBTNode<T> p = maximum(mRoot);
        if (p != null)
            return p.key;

        return null;
    }

    /* 
     * 找結(jié)點(x)的后繼結(jié)點。即,查找"紅黑樹中數(shù)據(jù)值大于該結(jié)點"的"最小結(jié)點"。
     */
    public RBTNode<T> successor(RBTNode<T> x) {
        // 如果x存在右孩子,則"x的后繼結(jié)點"為 "以其右孩子為根的子樹的最小結(jié)點"。
        if (x.right != null)
            return minimum(x.right);

        // 如果x沒有右孩子。則x有以下兩種可能:
        // (01) x是"一個左孩子",則"x的后繼結(jié)點"為 "它的父結(jié)點"。
        // (02) x是"一個右孩子",則查找"x的最低的父結(jié)點,并且該父結(jié)點要具有左孩子",找到的這個"最低的父結(jié)點"就是"x的后繼結(jié)點"。
        RBTNode<T> y = x.parent;
        while ((y!=null) && (x==y.right)) {
            x = y;
            y = y.parent;
        }

        return y;
    }
     
    /* 
     * 找結(jié)點(x)的前驅(qū)結(jié)點。即,查找"紅黑樹中數(shù)據(jù)值小于該結(jié)點"的"最大結(jié)點"。
     */
    public RBTNode<T> predecessor(RBTNode<T> x) {
        // 如果x存在左孩子,則"x的前驅(qū)結(jié)點"為 "以其左孩子為根的子樹的最大結(jié)點"。
        if (x.left != null)
            return maximum(x.left);

        // 如果x沒有左孩子。則x有以下兩種可能:
        // (01) x是"一個右孩子",則"x的前驅(qū)結(jié)點"為 "它的父結(jié)點"。
        // (01) x是"一個左孩子",則查找"x的最低的父結(jié)點,并且該父結(jié)點要具有右孩子",找到的這個"最低的父結(jié)點"就是"x的前驅(qū)結(jié)點"。
        RBTNode<T> y = x.parent;
        while ((y!=null) && (x==y.left)) {
            x = y;
            y = y.parent;
        }

        return y;
    }

    /* 
     * 對紅黑樹的節(jié)點(x)進(jìn)行左旋轉(zhuǎn)
     *
     * 左旋示意圖(對節(jié)點x進(jìn)行左旋):
     *      px                              px
     *     /                               /
     *    x                               y                
     *   /  \      --(左旋)-.           / \                #
     *  lx   y                          x  ry     
     *     /   \                       /  \
     *    ly   ry                     lx  ly  
     *
     *
     */
    private void leftRotate(RBTNode<T> x) {
        // 設(shè)置x的右孩子為y
        RBTNode<T> y = x.right;

        // 將 “y的左孩子” 設(shè)為 “x的右孩子”;
        // 如果y的左孩子非空,將 “x” 設(shè)為 “y的左孩子的父親”
        x.right = y.left;
        if (y.left != null)
            y.left.parent = x;

        // 將 “x的父親” 設(shè)為 “y的父親”
        y.parent = x.parent;

        if (x.parent == null) {
            this.mRoot = y;            // 如果 “x的父親” 是空節(jié)點,則將y設(shè)為根節(jié)點
        } else {
            if (x.parent.left == x)
                x.parent.left = y;    // 如果 x是它父節(jié)點的左孩子,則將y設(shè)為“x的父節(jié)點的左孩子”
            else
                x.parent.right = y;    // 如果 x是它父節(jié)點的左孩子,則將y設(shè)為“x的父節(jié)點的左孩子”
        }
        
        // 將 “x” 設(shè)為 “y的左孩子”
        y.left = x;
        // 將 “x的父節(jié)點” 設(shè)為 “y”
        x.parent = y;
    }

    /* 
     * 對紅黑樹的節(jié)點(y)進(jìn)行右旋轉(zhuǎn)
     *
     * 右旋示意圖(對節(jié)點y進(jìn)行左旋):
     *            py                               py
     *           /                                /
     *          y                                x                  
     *         /  \      --(右旋)-.            /  \                     #
     *        x   ry                           lx   y  
     *       / \                                   / \                   #
     *      lx  rx                                rx  ry
     * 
     */
    private void rightRotate(RBTNode<T> y) {
        // 設(shè)置x是當(dāng)前節(jié)點的左孩子。
        RBTNode<T> x = y.left;

        // 將 “x的右孩子” 設(shè)為 “y的左孩子”;
        // 如果"x的右孩子"不為空的話,將 “y” 設(shè)為 “x的右孩子的父親”
        y.left = x.right;
        if (x.right != null)
            x.right.parent = y;

        // 將 “y的父親” 設(shè)為 “x的父親”
        x.parent = y.parent;

        if (y.parent == null) {
            this.mRoot = x;            // 如果 “y的父親” 是空節(jié)點,則將x設(shè)為根節(jié)點
        } else {
            if (y == y.parent.right)
                y.parent.right = x;    // 如果 y是它父節(jié)點的右孩子,則將x設(shè)為“y的父節(jié)點的右孩子”
            else
                y.parent.left = x;    // (y是它父節(jié)點的左孩子) 將x設(shè)為“x的父節(jié)點的左孩子”
        }

        // 將 “y” 設(shè)為 “x的右孩子”
        x.right = y;

        // 將 “y的父節(jié)點” 設(shè)為 “x”
        y.parent = x;
    }

    /*
     * 紅黑樹插入修正函數(shù)
     *
     * 在向紅黑樹中插入節(jié)點之后(失去平衡),再調(diào)用該函數(shù);
     * 目的是將它重新塑造成一顆紅黑樹。
     *
     * 參數(shù)說明:
     *     node 插入的結(jié)點        // 對應(yīng)《算法導(dǎo)論》中的z
     */
    private void insertFixUp(RBTNode<T> node) {
        RBTNode<T> parent, gparent;

        // 若“父節(jié)點存在,并且父節(jié)點的顏色是紅色”
        while (((parent = parentOf(node))!=null) && isRed(parent)) {
            gparent = parentOf(parent);

            //若“父節(jié)點”是“祖父節(jié)點的左孩子”
            if (parent == gparent.left) {
                // Case 1條件:叔叔節(jié)點是紅色
                RBTNode<T> uncle = gparent.right;
                if ((uncle!=null) && isRed(uncle)) {
                    setBlack(uncle);
                    setBlack(parent);
                    setRed(gparent);
                    node = gparent;
                    continue;
                }

                // Case 2條件:叔叔是黑色,且當(dāng)前節(jié)點是右孩子
                if (parent.right == node) {
                    RBTNode<T> tmp;
                    leftRotate(parent);
                    tmp = parent;
                    parent = node;
                    node = tmp;
                }

                // Case 3條件:叔叔是黑色,且當(dāng)前節(jié)點是左孩子。
                setBlack(parent);
                setRed(gparent);
                rightRotate(gparent);
            } else {    //若“z的父節(jié)點”是“z的祖父節(jié)點的右孩子”
                // Case 1條件:叔叔節(jié)點是紅色
                RBTNode<T> uncle = gparent.left;
                if ((uncle!=null) && isRed(uncle)) {
                    setBlack(uncle);
                    setBlack(parent);
                    setRed(gparent);
                    node = gparent;
                    continue;
                }

                // Case 2條件:叔叔是黑色,且當(dāng)前節(jié)點是左孩子
                if (parent.left == node) {
                    RBTNode<T> tmp;
                    rightRotate(parent);
                    tmp = parent;
                    parent = node;
                    node = tmp;
                }

                // Case 3條件:叔叔是黑色,且當(dāng)前節(jié)點是右孩子。
                setBlack(parent);
                setRed(gparent);
                leftRotate(gparent);
            }
        }

        // 將根節(jié)點設(shè)為黑色
        setBlack(this.mRoot);
    }

    /* 
     * 將結(jié)點插入到紅黑樹中
     *
     * 參數(shù)說明:
     *     node 插入的結(jié)點        // 對應(yīng)《算法導(dǎo)論》中的node
     */
    private void insert(RBTNode<T> node) {
        int cmp;
        RBTNode<T> y = null;
        RBTNode<T> x = this.mRoot;

        // 1. 將紅黑樹當(dāng)作一顆二叉查找樹,將節(jié)點添加到二叉查找樹中。
        while (x != null) {
            y = x;
            cmp = node.key.compareTo(x.key);
            if (cmp < 0)
                x = x.left;
            else
                x = x.right;
        }

        node.parent = y;
        if (y!=null) {
            cmp = node.key.compareTo(y.key);
            if (cmp < 0)
                y.left = node;
            else
                y.right = node;
        } else {
            this.mRoot = node;
        }

        // 2. 設(shè)置節(jié)點的顏色為紅色
        node.color = RED;

        // 3. 將它重新修正為一顆二叉查找樹
        insertFixUp(node);
    }

    /* 
     * 新建結(jié)點(key),并將其插入到紅黑樹中
     *
     * 參數(shù)說明:
     *     key 插入結(jié)點的鍵值
     */
    public void insert(T key) {
        RBTNode<T> node=new RBTNode<T>(key,BLACK,null,null,null);

        // 如果新建結(jié)點失敗,則返回。
        if (node != null)
            insert(node);
    }


    /*
     * 紅黑樹刪除修正函數(shù)
     *
     * 在從紅黑樹中刪除插入節(jié)點之后(紅黑樹失去平衡),再調(diào)用該函數(shù);
     * 目的是將它重新塑造成一顆紅黑樹。
     *
     * 參數(shù)說明:
     *     node 待修正的節(jié)點
     */
    private void removeFixUp(RBTNode<T> node, RBTNode<T> parent) {
        RBTNode<T> other;

        while ((node==null || isBlack(node)) && (node != this.mRoot)) {
            if (parent.left == node) {
                other = parent.right;
                if (isRed(other)) {
                    // Case 1: x的兄弟w是紅色的  
                    setBlack(other);
                    setRed(parent);
                    leftRotate(parent);
                    other = parent.right;
                }

                if ((other.left==null || isBlack(other.left)) &&
                    (other.right==null || isBlack(other.right))) {
                    // Case 2: x的兄弟w是黑色,且w的倆個孩子也都是黑色的  
                    setRed(other);
                    node = parent;
                    parent = parentOf(node);
                } else {

                    if (other.right==null || isBlack(other.right)) {
                        // Case 3: x的兄弟w是黑色的,并且w的左孩子是紅色,右孩子為黑色。  
                        setBlack(other.left);
                        setRed(other);
                        rightRotate(other);
                        other = parent.right;
                    }
                    // Case 4: x的兄弟w是黑色的;并且w的右孩子是紅色的,左孩子任意顏色。
                    setColor(other, colorOf(parent));
                    setBlack(parent);
                    setBlack(other.right);
                    leftRotate(parent);
                    node = this.mRoot;
                    break;
                }
            } else {

                other = parent.left;
                if (isRed(other)) {
                    // Case 1: x的兄弟w是紅色的  
                    setBlack(other);
                    setRed(parent);
                    rightRotate(parent);
                    other = parent.left;
                }

                if ((other.left==null || isBlack(other.left)) &&
                    (other.right==null || isBlack(other.right))) {
                    // Case 2: x的兄弟w是黑色,且w的倆個孩子也都是黑色的  
                    setRed(other);
                    node = parent;
                    parent = parentOf(node);
                } else {

                    if (other.left==null || isBlack(other.left)) {
                        // Case 3: x的兄弟w是黑色的,并且w的左孩子是紅色,右孩子為黑色。  
                        setBlack(other.right);
                        setRed(other);
                        leftRotate(other);
                        other = parent.left;
                    }

                    // Case 4: x的兄弟w是黑色的;并且w的右孩子是紅色的,左孩子任意顏色。
                    setColor(other, colorOf(parent));
                    setBlack(parent);
                    setBlack(other.left);
                    rightRotate(parent);
                    node = this.mRoot;
                    break;
                }
            }
        }

        if (node!=null)
            setBlack(node);
    }

    /* 
     * 刪除結(jié)點(node),并返回被刪除的結(jié)點
     *
     * 參數(shù)說明:
     *     node 刪除的結(jié)點
     */
    private void remove(RBTNode<T> node) {
        RBTNode<T> child, parent;
        boolean color;

        // 被刪除節(jié)點的"左右孩子都不為空"的情況。
        if ( (node.left!=null) && (node.right!=null) ) {
            // 被刪節(jié)點的后繼節(jié)點。(稱為"取代節(jié)點")
            // 用它來取代"被刪節(jié)點"的位置,然后再將"被刪節(jié)點"去掉。
            RBTNode<T> replace = node;

            // 獲取后繼節(jié)點
            replace = replace.right;
            while (replace.left != null)
                replace = replace.left;

            // "node節(jié)點"不是根節(jié)點(只有根節(jié)點不存在父節(jié)點)
            if (parentOf(node)!=null) {
                if (parentOf(node).left == node)
                    parentOf(node).left = replace;
                else
                    parentOf(node).right = replace;
            } else {
                // "node節(jié)點"是根節(jié)點,更新根節(jié)點。
                this.mRoot = replace;
            }

            // child是"取代節(jié)點"的右孩子,也是需要"調(diào)整的節(jié)點"。
            // "取代節(jié)點"肯定不存在左孩子!因為它是一個后繼節(jié)點。
            child = replace.right;
            parent = parentOf(replace);
            // 保存"取代節(jié)點"的顏色
            color = colorOf(replace);

            // "被刪除節(jié)點"是"它的后繼節(jié)點的父節(jié)點"
            if (parent == node) {
                parent = replace;
            } else {
                // child不為空
                if (child!=null)
                    setParent(child, parent);
                parent.left = child;

                replace.right = node.right;
                setParent(node.right, replace);
            }

            replace.parent = node.parent;
            replace.color = node.color;
            replace.left = node.left;
            node.left.parent = replace;

            if (color == BLACK)
                removeFixUp(child, parent);

            node = null;
            return ;
        }

        if (node.left !=null) {
            child = node.left;
        } else {
            child = node.right;
        }

        parent = node.parent;
        // 保存"取代節(jié)點"的顏色
        color = node.color;

        if (child!=null)
            child.parent = parent;

        // "node節(jié)點"不是根節(jié)點
        if (parent!=null) {
            if (parent.left == node)
                parent.left = child;
            else
                parent.right = child;
        } else {
            this.mRoot = child;
        }

        if (color == BLACK)
            removeFixUp(child, parent);
        node = null;
    }

    /* 
     * 刪除結(jié)點(z),并返回被刪除的結(jié)點
     *
     * 參數(shù)說明:
     *     tree 紅黑樹的根結(jié)點
     *     z 刪除的結(jié)點
     */
    public void remove(T key) {
        RBTNode<T> node; 

        if ((node = search(mRoot, key)) != null)
            remove(node);
    }

    /*
     * 銷毀紅黑樹
     */
    private void destroy(RBTNode<T> tree) {
        if (tree==null)
            return ;

        if (tree.left != null)
            destroy(tree.left);
        if (tree.right != null)
            destroy(tree.right);

        tree=null;
    }

    public void clear() {
        destroy(mRoot);
        mRoot = null;
    }

    /*
     * 打印"紅黑樹"
     *
     * key        -- 節(jié)點的鍵值 
     * direction  --  0,表示該節(jié)點是根節(jié)點;
     *               -1,表示該節(jié)點是它的父結(jié)點的左孩子;
     *                1,表示該節(jié)點是它的父結(jié)點的右孩子。
     */
    private void print(RBTNode<T> tree, T key, int direction) {

        if(tree != null) {

            if(direction==0)    // tree是根節(jié)點
                System.out.printf("%2d(B) is root\n", tree.key);
            else                // tree是分支節(jié)點
                System.out.printf("%2d(%s) is %2d's %6s child\n", tree.key, isRed(tree)?"R":"B", key, direction==1?"right" : "left");

            print(tree.left, tree.key, -1);
            print(tree.right,tree.key,  1);
        }
    }

    public void print() {
        if (mRoot != null)
            print(mRoot, mRoot.key, 0);
    }
}

紅黑樹的測試文件(RBTreeTest.java)

/**
 * Java 語言: 二叉查找樹
 *
 * @author skywang
 * @date 2013/11/07
 */
public class RBTreeTest {

    private static final int a[] = {10, 40, 30, 60, 90, 70, 20, 50, 80};
    private static final boolean mDebugInsert = false;    // "插入"動作的檢測開關(guān)(false,關(guān)閉;true,打開)
    private static final boolean mDebugDelete = false;    // "刪除"動作的檢測開關(guān)(false,關(guān)閉;true,打開)

    public static void main(String[] args) {
        int i, ilen = a.length;
        RBTree<Integer> tree=new RBTree<Integer>();

        System.out.printf("== 原始數(shù)據(jù): ");
        for(i=0; i<ilen; i++)
            System.out.printf("%d ", a[i]);
        System.out.printf("\n");

        for(i=0; i<ilen; i++) {
            tree.insert(a[i]);
            // 設(shè)置mDebugInsert=true,測試"添加函數(shù)"
            if (mDebugInsert) {
                System.out.printf("== 添加節(jié)點: %d\n", a[i]);
                System.out.printf("== 樹的詳細(xì)信息: \n");
                tree.print();
                System.out.printf("\n");
            }
        }

        System.out.printf("== 前序遍歷: ");
        tree.preOrder();

        System.out.printf("\n== 中序遍歷: ");
        tree.inOrder();

        System.out.printf("\n== 后序遍歷: ");
        tree.postOrder();
        System.out.printf("\n");

        System.out.printf("== 最小值: %s\n", tree.minimum());
        System.out.printf("== 最大值: %s\n", tree.maximum());
        System.out.printf("== 樹的詳細(xì)信息: \n");
        tree.print();
        System.out.printf("\n");

        // 設(shè)置mDebugDelete=true,測試"刪除函數(shù)"
        if (mDebugDelete) {
            for(i=0; i<ilen; i++)
            {
                tree.remove(a[i]);

                System.out.printf("== 刪除節(jié)點: %d\n", a[i]);
                System.out.printf("== 樹的詳細(xì)信息: \n");
                tree.print();
                System.out.printf("\n");
            }
        }

        // 銷毀二叉樹
        tree.clear();
    }
}

參考資料

真的很感謝這位博主的解析,讓我等也能窺探紅黑樹的代碼魅力。
1.http://www.cnblogs.com/skywang12345/p/3624343.htmle

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 0.目錄 1.算法導(dǎo)論的紅黑樹本質(zhì)上是2-3-4樹 2.紅黑樹的結(jié)構(gòu)和性質(zhì) 3.紅黑樹的插入 4.紅黑樹的刪除 5...
    王偵閱讀 2,752評論 1 2
  • 數(shù)據(jù)結(jié)構(gòu)與算法--從平衡二叉樹(AVL)到紅黑樹 上節(jié)學(xué)習(xí)了二叉查找樹。算法的性能取決于樹的形狀,而樹的形狀取決于...
    sunhaiyu閱讀 7,819評論 4 32
  • 這個周看算法導(dǎo)論,看到紅黑樹,看的我云里霧里繞啊。雖然最后看懂了,據(jù)我估計,要是過一個星期不看保證忘干凈,因此決定...
    充滿活力的早晨閱讀 2,086評論 0 3
  • R-B Tree簡介 R-B Tree,全稱是Red-Black Tree,又稱為“紅黑樹”,它一種特殊的二叉查找...
    張晨輝Allen閱讀 9,556評論 5 30
  • 一. 算法之變,結(jié)構(gòu)為宗 計算機(jī)在很多情況下被應(yīng)用于檢索數(shù)據(jù),比如航空和鐵路運輸業(yè)的航班信息和列車時刻表的查詢,都...
    Leesper閱讀 7,411評論 13 42

友情鏈接更多精彩內(nèi)容