紅黑樹(五)之 Java的實現

 

概要

前面分別介紹紅黑樹的理論知識、紅黑樹的C語言C++的實現。本章介紹紅黑樹的Java實現,若讀者對紅黑樹的理論知識不熟悉,創建先學習紅黑樹的理論知識,再來學習本章。仍是那句老話,紅黑樹的C/C++/Java實現,原理同樣,擇其一瞭解便可。html

目錄
1. 紅黑樹的介紹
2. 紅黑樹的Java實現(代碼說明)
3. 紅黑樹的Java實現(完整源碼)
4. 紅黑樹的Java測試程序java

轉載請註明出處:node


更多內容:數據結構與算法系列 目錄算法

(01) 紅黑樹(一)之 原理和算法詳細介紹
(02) 紅黑樹(二)之 C語言的實現
(03) 紅黑樹(三)之 Linux內核中紅黑樹的經典實現
(04) 紅黑樹(四)之 C++的實現 
(05) 紅黑樹(五)之 Java的實現
(06) 紅黑樹(六)之 參考資料
數據結構

 

紅黑樹的介紹

紅黑樹(Red-Black Tree,簡稱R-B Tree),它一種特殊的二叉查找樹。
紅黑樹是特殊的二叉查找樹,意味着它知足二叉查找樹的特徵:任意一個節點所包含的鍵值,大於等於左孩子的鍵值,小於等於右孩子的鍵值。
除了具有該特性以外,紅黑樹還包括許多額外的信息。ide

紅黑樹的每一個節點上都有存儲位表示節點的顏色,顏色是紅(Red)或黑(Black)。
紅黑樹的特性:
(1) 每一個節點或者是黑色,或者是紅色。
(2) 根節點是黑色。
(3) 每一個葉子節點是黑色。 [注意:這裏葉子節點,是指爲空的葉子節點!]
(4) 若是一個節點是紅色的,則它的子節點必須是黑色的。
(5) 從一個節點到該節點的子孫節點的全部路徑上包含相同數目的黑節點。函數

關於它的特性,須要注意的是:
第一,特性(3)中的葉子節點,是隻爲空(NIL或null)的節點。
第二,特性(5),確保沒有一條路徑會比其餘路徑長出倆倍。於是,紅黑樹是相對是接近平衡的二叉樹。post

紅黑樹示意圖以下:學習

 

紅黑樹的Java實現(代碼說明)

紅黑樹的基本操做是添加刪除旋轉。在對紅黑樹進行添加或刪除後,會用到旋轉方法。爲何呢?道理很簡單,添加或刪除紅黑樹中的節點以後,紅黑樹就發生了變化,可能不知足紅黑樹的5條性質,也就再也不是一顆紅黑樹了,而是一顆普通的樹。而經過旋轉,可使這顆樹從新成爲紅黑樹。簡單點說,旋轉的目的是讓樹保持紅黑樹的特性。
旋轉包括兩種:左旋右旋。下面分別對紅黑樹的基本操做進行介紹。測試

 

1. 基本定義

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

    private RBTNode<T> mRoot;    // 根結點

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

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

        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是紅黑樹對應的類,RBTNode是紅黑樹的節點類。在RBTree中包含了根節點mRoot和紅黑樹的相關API。
注意:在實現紅黑樹API的過程當中,我重載了許多函數。重載的緣由,一是由於有的API是內部接口,有的是外部接口;二是爲了讓結構更加清晰。

 

2. 左旋

對x進行左旋,意味着"將x變成一個左節點"。

 

左旋的實現代碼(Java語言)

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

    // 將 「y的左孩子」 設爲 「x的右孩子」;
    // 若是y的左孩子非空,將 「x」 設爲 「y的左孩子的父親」
    x.right = y.left;
    if (y.left != null)
        y.left.parent = x;

    // 將 「x的父親」 設爲 「y的父親」
    y.parent = x.parent;

    if (x.parent == null) {
        this.mRoot = y;            // 若是 「x的父親」 是空節點,則將y設爲根節點
    } else {
        if (x.parent.left == x)
            x.parent.left = y;    // 若是 x是它父節點的左孩子,則將y設爲「x的父節點的左孩子」
        else
            x.parent.right = y;    // 若是 x是它父節點的左孩子,則將y設爲「x的父節點的左孩子」
    }
    
    // 將 「x」 設爲 「y的左孩子」
    y.left = x;
    // 將 「x的父節點」 設爲 「y」
    x.parent = y;
}

 

3. 右旋

對y進行左旋,意味着"將y變成一個右節點"。

 

右旋的實現代碼(Java語言)

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

    // 將 「x的右孩子」 設爲 「y的左孩子」;
    // 若是"x的右孩子"不爲空的話,將 「y」 設爲 「x的右孩子的父親」
    y.left = x.right;
    if (x.right != null)
        x.right.parent = y;

    // 將 「y的父親」 設爲 「x的父親」
    x.parent = y.parent;

    if (y.parent == null) {
        this.mRoot = x;            // 若是 「y的父親」 是空節點,則將x設爲根節點
    } else {
        if (y == y.parent.right)
            y.parent.right = x;    // 若是 y是它父節點的右孩子,則將x設爲「y的父節點的右孩子」
        else
            y.parent.left = x;    // (y是它父節點的左孩子) 將x設爲「x的父節點的左孩子」
    }

    // 將 「y」 設爲 「x的右孩子」
    x.right = y;

    // 將 「y的父節點」 設爲 「x」
    y.parent = x;
}

 

4. 添加

將一個節點插入到紅黑樹中,須要執行哪些步驟呢?首先,將紅黑樹看成一顆二叉查找樹,將節點插入;而後,將節點着色爲紅色;最後,經過"旋轉和從新着色"等一系列操做來修正該樹,使之從新成爲一顆紅黑樹。詳細描述以下:
第一步: 將紅黑樹看成一顆二叉查找樹,將節點插入。
       紅黑樹自己就是一顆二叉查找樹,將節點插入後,該樹仍然是一顆二叉查找樹。也就意味着,樹的鍵值仍然是有序的。此外,不管是左旋仍是右旋,若旋轉以前這棵樹是二叉查找樹,旋轉以後它必定仍是二叉查找樹。這也就意味着,任何的旋轉和從新着色操做,都不會改變它仍然是一顆二叉查找樹的事實。
好吧?那接下來,咱們就來千方百計的旋轉以及從新着色,使這顆樹從新成爲紅黑樹!

第二步:將插入的節點着色爲"紅色"。
       爲何着色成紅色,而不是黑色呢?爲何呢?在回答以前,咱們須要從新溫習一下紅黑樹的特性:
(1) 每一個節點或者是黑色,或者是紅色。
(2) 根節點是黑色。
(3) 每一個葉子節點是黑色。 [注意:這裏葉子節點,是指爲空的葉子節點!]
(4) 若是一個節點是紅色的,則它的子節點必須是黑色的。
(5) 從一個節點到該節點的子孫節點的全部路徑上包含相同數目的黑節點。
      將插入的節點着色爲紅色,不會違背"特性(5)"!少違背一條特性,就意味着咱們須要處理的狀況越少。接下來,就要努力的讓這棵樹知足其它性質便可;知足了的話,它就又是一顆紅黑樹了。o(∩∩)o...哈哈

第三步: 經過一系列的旋轉或着色等操做,使之從新成爲一顆紅黑樹。
       第二步中,將插入節點着色爲"紅色"以後,不會違背"特性(5)"。那它到底會違背哪些特性呢?
       對於"特性(1)",顯然不會違背了。由於咱們已經將它塗成紅色了。
       對於"特性(2)",顯然也不會違背。在第一步中,咱們是將紅黑樹看成二叉查找樹,而後執行的插入操做。而根據二叉查找數的特色,插入操做不會改變根節點。因此,根節點仍然是黑色。
       對於"特性(3)",顯然不會違背了。這裏的葉子節點是指的空葉子節點,插入非空節點並不會對它們形成影響。
       對於"特性(4)",是有可能違背的!
       那接下來,想辦法使之"知足特性(4)",就能夠將樹從新構形成紅黑樹了。


添加操做的實現代碼(Java語言)

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

    // 1. 將紅黑樹看成一顆二叉查找樹,將節點添加到二叉查找樹中。
    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. 設置節點的顏色爲紅色
    node.color = RED;

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

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

    // 若是新建結點失敗,則返回。
    if (node != null)
        insert(node);
}

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


添加修正操做的實現代碼(Java語言)

/*
 * 紅黑樹插入修正函數
 *
 * 在向紅黑樹中插入節點以後(失去平衡),再調用該函數;
 * 目的是將它從新塑形成一顆紅黑樹。
 *
 * 參數說明:
 *     node 插入的結點        // 對應《算法導論》中的z
 */
private void insertFixUp(RBTNode<T> node) {
    RBTNode<T> parent, gparent;

    // 若「父節點存在,而且父節點的顏色是紅色」
    while (((parent = parentOf(node))!=null) && isRed(parent)) {
        gparent = parentOf(parent);

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

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

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

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

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

    // 將根節點設爲黑色
    setBlack(this.mRoot);
}

insertFixUp(node)的做用是對應"上面所講的第三步"。它是一個內部接口。

 

5. 刪除操做

將紅黑樹內的某一個節點刪除。須要執行的操做依次是:首先,將紅黑樹看成一顆二叉查找樹,將該節點從二叉查找樹中刪除;而後,經過"旋轉和從新着色"等一系列來修正該樹,使之從新成爲一棵紅黑樹。詳細描述以下:
第一步:將紅黑樹看成一顆二叉查找樹,將節點刪除。
       這和"刪除常規二叉查找樹中刪除節點的方法是同樣的"。分3種狀況:
① 被刪除節點沒有兒子,即爲葉節點。那麼,直接將該節點刪除就OK了。
② 被刪除節點只有一個兒子。那麼,直接刪除該節點,並用該節點的惟一子節點頂替它的位置。
③ 被刪除節點有兩個兒子。那麼,先找出它的後繼節點;而後把「它的後繼節點的內容」複製給「該節點的內容」;以後,刪除「它的後繼節點」。在這裏,後繼節點至關於替身,在將後繼節點的內容複製給"被刪除節點"以後,再將後繼節點刪除。這樣就巧妙的將問題轉換爲"刪除後繼節點"的狀況了,下面就考慮後繼節點。 在"被刪除節點"有兩個非空子節點的狀況下,它的後繼節點不多是雙子非空。既然"的後繼節點"不可能雙子都非空,就意味着"該節點的後繼節點"要麼沒有兒子,要麼只有一個兒子。若沒有兒子,則按"狀況① "進行處理;若只有一個兒子,則按"狀況② "進行處理。

第二步:經過"旋轉和從新着色"等一系列來修正該樹,使之從新成爲一棵紅黑樹。
        由於"第一步"中刪除節點以後,可能會違背紅黑樹的特性。因此須要經過"旋轉和從新着色"來修正該樹,使之從新成爲一棵紅黑樹。

 

刪除操做的實現代碼(Java語言)

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

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

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

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

        // child是"取代節點"的右孩子,也是須要"調整的節點"。
        // "取代節點"確定不存在左孩子!由於它是一個後繼節點。
        child = replace.right;
        parent = parentOf(replace);
        // 保存"取代節點"的顏色
        color = colorOf(replace);

        // "被刪除節點"是"它的後繼節點的父節點"
        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;
    // 保存"取代節點"的顏色
    color = node.color;

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

    // "node節點"不是根節點
    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;
}

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

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

內部接口 -- remove(node)的做用是將"node"節點插入到紅黑樹中。
外部接口 -- remove(key)刪除紅黑樹中鍵值爲key的節點。


刪除修正操做的實現代碼(Java語言)

/*
 * 紅黑樹刪除修正函數
 *
 * 在從紅黑樹中刪除插入節點以後(紅黑樹失去平衡),再調用該函數;
 * 目的是將它從新塑形成一顆紅黑樹。
 *
 * 參數說明:
 *     node 待修正的節點
 */
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)是對應"上面所講的第三步"。它是一個內部接口。

 

紅黑樹的Java實現(完整源碼)

下面是紅黑樹實現的完整代碼和相應的測試程序。
(1) 除了上面所說的"左旋"、"右旋"、"添加"、"刪除"等基本操做以後,還實現了"遍歷"、"查找"、"打印"、"最小值"、"最大值"、"建立"、"銷燬"等接口。
(2) 函數接口大多分爲內部接口和外部接口。內部接口是private函數,外部接口則是public函數。
(3) 測試代碼中提供了"插入"和"刪除"動做的檢測開關。默認是關閉的,打開方法能夠參考"代碼中的說明"。建議在打開開關後,在草稿上本身動手繪製一下紅黑樹。

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

  1 /**
  2  * Java 語言: 紅黑樹
  3  *
  4  * @author skywang
  5  * @date 2013/11/07
  6  */
  7 
  8 public class RBTree<T extends Comparable<T>> {
  9 
 10     private RBTNode<T> mRoot;    // 根結點
 11 
 12     private static final boolean RED   = false;
 13     private static final boolean BLACK = true;
 14 
 15     public class RBTNode<T extends Comparable<T>> {
 16         boolean color;        // 顏色
 17         T key;                // 關鍵字(鍵值)
 18         RBTNode<T> left;    // 左孩子
 19         RBTNode<T> right;    // 右孩子
 20         RBTNode<T> parent;    // 父結點
 21 
 22         public RBTNode(T key, boolean color, RBTNode<T> parent, RBTNode<T> left, RBTNode<T> right) {
 23             this.key = key;
 24             this.color = color;
 25             this.parent = parent;
 26             this.left = left;
 27             this.right = right;
 28         }
 29 
 30         public T getKey() {
 31             return key;
 32         }
 33 
 34         public String toString() {
 35             return ""+key+(this.color==RED?"(R)":"B");
 36         }
 37     }
 38 
 39     public RBTree() {
 40         mRoot=null;
 41     }
 42 
 43     private RBTNode<T> parentOf(RBTNode<T> node) {
 44         return node!=null ? node.parent : null;
 45     }
 46     private boolean colorOf(RBTNode<T> node) {
 47         return node!=null ? node.color : BLACK;
 48     }
 49     private boolean isRed(RBTNode<T> node) {
 50         return ((node!=null)&&(node.color==RED)) ? true : false;
 51     }
 52     private boolean isBlack(RBTNode<T> node) {
 53         return !isRed(node);
 54     }
 55     private void setBlack(RBTNode<T> node) {
 56         if (node!=null)
 57             node.color = BLACK;
 58     }
 59     private void setRed(RBTNode<T> node) {
 60         if (node!=null)
 61             node.color = RED;
 62     }
 63     private void setParent(RBTNode<T> node, RBTNode<T> parent) {
 64         if (node!=null)
 65             node.parent = parent;
 66     }
 67     private void setColor(RBTNode<T> node, boolean color) {
 68         if (node!=null)
 69             node.color = color;
 70     }
 71 
 72     /*
 73      * 前序遍歷"紅黑樹"
 74      */
 75     private void preOrder(RBTNode<T> tree) {
 76         if(tree != null) {
 77             System.out.print(tree.key+" ");
 78             preOrder(tree.left);
 79             preOrder(tree.right);
 80         }
 81     }
 82 
 83     public void preOrder() {
 84         preOrder(mRoot);
 85     }
 86 
 87     /*
 88      * 中序遍歷"紅黑樹"
 89      */
 90     private void inOrder(RBTNode<T> tree) {
 91         if(tree != null) {
 92             inOrder(tree.left);
 93             System.out.print(tree.key+" ");
 94             inOrder(tree.right);
 95         }
 96     }
 97 
 98     public void inOrder() {
 99         inOrder(mRoot);
100     }
101 
102 
103     /*
104      * 後序遍歷"紅黑樹"
105      */
106     private void postOrder(RBTNode<T> tree) {
107         if(tree != null)
108         {
109             postOrder(tree.left);
110             postOrder(tree.right);
111             System.out.print(tree.key+" ");
112         }
113     }
114 
115     public void postOrder() {
116         postOrder(mRoot);
117     }
118 
119 
120     /*
121      * (遞歸實現)查找"紅黑樹x"中鍵值爲key的節點
122      */
123     private RBTNode<T> search(RBTNode<T> x, T key) {
124         if (x==null)
125             return x;
126 
127         int cmp = key.compareTo(x.key);
128         if (cmp < 0)
129             return search(x.left, key);
130         else if (cmp > 0)
131             return search(x.right, key);
132         else
133             return x;
134     }
135 
136     public RBTNode<T> search(T key) {
137         return search(mRoot, key);
138     }
139 
140     /*
141      * (非遞歸實現)查找"紅黑樹x"中鍵值爲key的節點
142      */
143     private RBTNode<T> iterativeSearch(RBTNode<T> x, T key) {
144         while (x!=null) {
145             int cmp = key.compareTo(x.key);
146 
147             if (cmp < 0) 
148                 x = x.left;
149             else if (cmp > 0) 
150                 x = x.right;
151             else
152                 return x;
153         }
154 
155         return x;
156     }
157 
158     public RBTNode<T> iterativeSearch(T key) {
159         return iterativeSearch(mRoot, key);
160     }
161 
162     /* 
163      * 查找最小結點:返回tree爲根結點的紅黑樹的最小結點。
164      */
165     private RBTNode<T> minimum(RBTNode<T> tree) {
166         if (tree == null)
167             return null;
168 
169         while(tree.left != null)
170             tree = tree.left;
171         return tree;
172     }
173 
174     public T minimum() {
175         RBTNode<T> p = minimum(mRoot);
176         if (p != null)
177             return p.key;
178 
179         return null;
180     }
181      
182     /* 
183      * 查找最大結點:返回tree爲根結點的紅黑樹的最大結點。
184      */
185     private RBTNode<T> maximum(RBTNode<T> tree) {
186         if (tree == null)
187             return null;
188 
189         while(tree.right != null)
190             tree = tree.right;
191         return tree;
192     }
193 
194     public T maximum() {
195         RBTNode<T> p = maximum(mRoot);
196         if (p != null)
197             return p.key;
198 
199         return null;
200     }
201 
202     /* 
203      * 找結點(x)的後繼結點。即,查找"紅黑樹中數據值大於該結點"的"最小結點"。
204      */
205     public RBTNode<T> successor(RBTNode<T> x) {
206         // 若是x存在右孩子,則"x的後繼結點"爲 "以其右孩子爲根的子樹的最小結點"。
207         if (x.right != null)
208             return minimum(x.right);
209 
210         // 若是x沒有右孩子。則x有如下兩種可能:
211         // (01) x是"一個左孩子",則"x的後繼結點"爲 "它的父結點"。
212         // (02) x是"一個右孩子",則查找"x的最低的父結點,而且該父結點要具備左孩子",找到的這個"最低的父結點"就是"x的後繼結點"。
213         RBTNode<T> y = x.parent;
214         while ((y!=null) && (x==y.right)) {
215             x = y;
216             y = y.parent;
217         }
218 
219         return y;
220     }
221      
222     /* 
223      * 找結點(x)的前驅結點。即,查找"紅黑樹中數據值小於該結點"的"最大結點"。
224      */
225     public RBTNode<T> predecessor(RBTNode<T> x) {
226         // 若是x存在左孩子,則"x的前驅結點"爲 "以其左孩子爲根的子樹的最大結點"。
227         if (x.left != null)
228             return maximum(x.left);
229 
230         // 若是x沒有左孩子。則x有如下兩種可能:
231         // (01) x是"一個右孩子",則"x的前驅結點"爲 "它的父結點"。
232         // (01) x是"一個左孩子",則查找"x的最低的父結點,而且該父結點要具備右孩子",找到的這個"最低的父結點"就是"x的前驅結點"。
233         RBTNode<T> y = x.parent;
234         while ((y!=null) && (x==y.left)) {
235             x = y;
236             y = y.parent;
237         }
238 
239         return y;
240     }
241 
242     /* 
243      * 對紅黑樹的節點(x)進行左旋轉
244      *
245      * 左旋示意圖(對節點x進行左旋):
246      *      px                              px
247      *     /                               /
248      *    x                               y                
249      *   /  \      --(左旋)-.           / \                #
250      *  lx   y                          x  ry     
251      *     /   \                       /  \
252      *    ly   ry                     lx  ly  
253      *
254      *
255      */
256     private void leftRotate(RBTNode<T> x) {
257         // 設置x的右孩子爲y
258         RBTNode<T> y = x.right;
259 
260         // 將 「y的左孩子」 設爲 「x的右孩子」;
261         // 若是y的左孩子非空,將 「x」 設爲 「y的左孩子的父親」
262         x.right = y.left;
263         if (y.left != null)
264             y.left.parent = x;
265 
266         // 將 「x的父親」 設爲 「y的父親」
267         y.parent = x.parent;
268 
269         if (x.parent == null) {
270             this.mRoot = y;            // 若是 「x的父親」 是空節點,則將y設爲根節點
271         } else {
272             if (x.parent.left == x)
273                 x.parent.left = y;    // 若是 x是它父節點的左孩子,則將y設爲「x的父節點的左孩子」
274             else
275                 x.parent.right = y;    // 若是 x是它父節點的左孩子,則將y設爲「x的父節點的左孩子」
276         }
277         
278         // 將 「x」 設爲 「y的左孩子」
279         y.left = x;
280         // 將 「x的父節點」 設爲 「y」
281         x.parent = y;
282     }
283 
284     /* 
285      * 對紅黑樹的節點(y)進行右旋轉
286      *
287      * 右旋示意圖(對節點y進行左旋):
288      *            py                               py
289      *           /                                /
290      *          y                                x                  
291      *         /  \      --(右旋)-.            /  \                     #
292      *        x   ry                           lx   y  
293      *       / \                                   / \                   #
294      *      lx  rx                                rx  ry
295      * 
296      */
297     private void rightRotate(RBTNode<T> y) {
298         // 設置x是當前節點的左孩子。
299         RBTNode<T> x = y.left;
300 
301         // 將 「x的右孩子」 設爲 「y的左孩子」;
302         // 若是"x的右孩子"不爲空的話,將 「y」 設爲 「x的右孩子的父親」
303         y.left = x.right;
304         if (x.right != null)
305             x.right.parent = y;
306 
307         // 將 「y的父親」 設爲 「x的父親」
308         x.parent = y.parent;
309 
310         if (y.parent == null) {
311             this.mRoot = x;            // 若是 「y的父親」 是空節點,則將x設爲根節點
312         } else {
313             if (y == y.parent.right)
314                 y.parent.right = x;    // 若是 y是它父節點的右孩子,則將x設爲「y的父節點的右孩子」
315             else
316                 y.parent.left = x;    // (y是它父節點的左孩子) 將x設爲「x的父節點的左孩子」
317         }
318 
319         // 將 「y」 設爲 「x的右孩子」
320         x.right = y;
321 
322         // 將 「y的父節點」 設爲 「x」
323         y.parent = x;
324     }
325 
326     /*
327      * 紅黑樹插入修正函數
328      *
329      * 在向紅黑樹中插入節點以後(失去平衡),再調用該函數;
330      * 目的是將它從新塑形成一顆紅黑樹。
331      *
332      * 參數說明:
333      *     node 插入的結點        // 對應《算法導論》中的z
334      */
335     private void insertFixUp(RBTNode<T> node) {
336         RBTNode<T> parent, gparent;
337 
338         // 若「父節點存在,而且父節點的顏色是紅色」
339         while (((parent = parentOf(node))!=null) && isRed(parent)) {
340             gparent = parentOf(parent);
341 
342             //若「父節點」是「祖父節點的左孩子」
343             if (parent == gparent.left) {
344                 // Case 1條件:叔叔節點是紅色
345                 RBTNode<T> uncle = gparent.right;
346                 if ((uncle!=null) && isRed(uncle)) {
347                     setBlack(uncle);
348                     setBlack(parent);
349                     setRed(gparent);
350                     node = gparent;
351                     continue;
352                 }
353 
354                 // Case 2條件:叔叔是黑色,且當前節點是右孩子
355                 if (parent.right == node) {
356                     RBTNode<T> tmp;
357                     leftRotate(parent);
358                     tmp = parent;
359                     parent = node;
360                     node = tmp;
361                 }
362 
363                 // Case 3條件:叔叔是黑色,且當前節點是左孩子。
364                 setBlack(parent);
365                 setRed(gparent);
366                 rightRotate(gparent);
367             } else {    //若「z的父節點」是「z的祖父節點的右孩子」
368                 // Case 1條件:叔叔節點是紅色
369                 RBTNode<T> uncle = gparent.left;
370                 if ((uncle!=null) && isRed(uncle)) {
371                     setBlack(uncle);
372                     setBlack(parent);
373                     setRed(gparent);
374                     node = gparent;
375                     continue;
376                 }
377 
378                 // Case 2條件:叔叔是黑色,且當前節點是左孩子
379                 if (parent.left == node) {
380                     RBTNode<T> tmp;
381                     rightRotate(parent);
382                     tmp = parent;
383                     parent = node;
384                     node = tmp;
385                 }
386 
387                 // Case 3條件:叔叔是黑色,且當前節點是右孩子。
388                 setBlack(parent);
389                 setRed(gparent);
390                 leftRotate(gparent);
391             }
392         }
393 
394         // 將根節點設爲黑色
395         setBlack(this.mRoot);
396     }
397 
398     /* 
399      * 將結點插入到紅黑樹中
400      *
401      * 參數說明:
402      *     node 插入的結點        // 對應《算法導論》中的node
403      */
404     private void insert(RBTNode<T> node) {
405         int cmp;
406         RBTNode<T> y = null;
407         RBTNode<T> x = this.mRoot;
408 
409         // 1. 將紅黑樹看成一顆二叉查找樹,將節點添加到二叉查找樹中。
410         while (x != null) {
411             y = x;
412             cmp = node.key.compareTo(x.key);
413             if (cmp < 0)
414                 x = x.left;
415             else
416                 x = x.right;
417         }
418 
419         node.parent = y;
420         if (y!=null) {
421             cmp = node.key.compareTo(y.key);
422             if (cmp < 0)
423                 y.left = node;
424             else
425                 y.right = node;
426         } else {
427             this.mRoot = node;
428         }
429 
430         // 2. 設置節點的顏色爲紅色
431         node.color = RED;
432 
433         // 3. 將它從新修正爲一顆二叉查找樹
434         insertFixUp(node);
435     }
436 
437     /* 
438      * 新建結點(key),並將其插入到紅黑樹中
439      *
440      * 參數說明:
441      *     key 插入結點的鍵值
442      */
443     public void insert(T key) {
444         RBTNode<T> node=new RBTNode<T>(key,BLACK,null,null,null);
445 
446         // 若是新建結點失敗,則返回。
447         if (node != null)
448             insert(node);
449     }
450 
451 
452     /*
453      * 紅黑樹刪除修正函數
454      *
455      * 在從紅黑樹中刪除插入節點以後(紅黑樹失去平衡),再調用該函數;
456      * 目的是將它從新塑形成一顆紅黑樹。
457      *
458      * 參數說明:
459      *     node 待修正的節點
460      */
461     private void removeFixUp(RBTNode<T> node, RBTNode<T> parent) {
462         RBTNode<T> other;
463 
464         while ((node==null || isBlack(node)) && (node != this.mRoot)) {
465             if (parent.left == node) {
466                 other = parent.right;
467                 if (isRed(other)) {
468                     // Case 1: x的兄弟w是紅色的  
469                     setBlack(other);
470                     setRed(parent);
471                     leftRotate(parent);
472                     other = parent.right;
473                 }
474 
475                 if ((other.left==null || isBlack(other.left)) &&
476                     (other.right==null || isBlack(other.right))) {
477                     // Case 2: x的兄弟w是黑色,且w的倆個孩子也都是黑色的  
478                     setRed(other);
479                     node = parent;
480                     parent = parentOf(node);
481                 } else {
482 
483                     if (other.right==null || isBlack(other.right)) {
484                         // Case 3: x的兄弟w是黑色的,而且w的左孩子是紅色,右孩子爲黑色。  
485                         setBlack(other.left);
486                         setRed(other);
487                         rightRotate(other);
488                         other = parent.right;
489                     }
490                     // Case 4: x的兄弟w是黑色的;而且w的右孩子是紅色的,左孩子任意顏色。
491                     setColor(other, colorOf(parent));
492                     setBlack(parent);
493                     setBlack(other.right);
494                     leftRotate(parent);
495                     node = this.mRoot;
496                     break;
497                 }
498             } else {
499 
500                 other = parent.left;
501                 if (isRed(other)) {
502                     // Case 1: x的兄弟w是紅色的  
503                     setBlack(other);
504                     setRed(parent);
505                     rightRotate(parent);
506                     other = parent.left;
507                 }
508 
509                 if ((other.left==null || isBlack(other.left)) &&
510                     (other.right==null || isBlack(other.right))) {
511                     // Case 2: x的兄弟w是黑色,且w的倆個孩子也都是黑色的  
512                     setRed(other);
513                     node = parent;
514                     parent = parentOf(node);
515                 } else {
516 
517                     if (other.left==null || isBlack(other.left)) {
518                         // Case 3: x的兄弟w是黑色的,而且w的左孩子是紅色,右孩子爲黑色。  
519                         setBlack(other.right);
520                         setRed(other);
521                         leftRotate(other);
522                         other = parent.left;
523                     }
524 
525                     // Case 4: x的兄弟w是黑色的;而且w的右孩子是紅色的,左孩子任意顏色。
526                     setColor(other, colorOf(parent));
527                     setBlack(parent);
528                     setBlack(other.left);
529                     rightRotate(parent);
530                     node = this.mRoot;
531                     break;
532                 }
533             }
534         }
535 
536         if (node!=null)
537             setBlack(node);
538     }
539 
540     /* 
541      * 刪除結點(node),並返回被刪除的結點
542      *
543      * 參數說明:
544      *     node 刪除的結點
545      */
546     private void remove(RBTNode<T> node) {
547         RBTNode<T> child, parent;
548         boolean color;
549 
550         // 被刪除節點的"左右孩子都不爲空"的狀況。
551         if ( (node.left!=null) && (node.right!=null) ) {
552             // 被刪節點的後繼節點。(稱爲"取代節點")
553             // 用它來取代"被刪節點"的位置,而後再將"被刪節點"去掉。
554             RBTNode<T> replace = node;
555 
556             // 獲取後繼節點
557             replace = replace.right;
558             while (replace.left != null)
559                 replace = replace.left;
560 
561             // "node節點"不是根節點(只有根節點不存在父節點)
562             if (parentOf(node)!=null) {
563                 if (parentOf(node).left == node)
564                     parentOf(node).left = replace;
565                 else
566                     parentOf(node).right = replace;
567             } else {
568                 // "node節點"是根節點,更新根節點。
569                 this.mRoot = replace;
570             }
571 
572             // child是"取代節點"的右孩子,也是須要"調整的節點"。
573             // "取代節點"確定不存在左孩子!由於它是一個後繼節點。
574             child = replace.right;
575             parent = parentOf(replace);
576             // 保存"取代節點"的顏色
577             color = colorOf(replace);
578 
579             // "被刪除節點"是"它的後繼節點的父節點"
580             if (parent == node) {
581                 parent = replace;
582             } else {
583                 // child不爲空
584                 if (child!=null)
585                     setParent(child, parent);
586                 parent.left = child;
587 
588                 replace.right = node.right;
589                 setParent(node.right, replace);
590             }
591 
592             replace.parent = node.parent;
593             replace.color = node.color;
594             replace.left = node.left;
595             node.left.parent = replace;
596 
597             if (color == BLACK)
598                 removeFixUp(child, parent);
599 
600             node = null;
601             return ;
602         }
603 
604         if (node.left !=null) {
605             child = node.left;
606         } else {
607             child = node.right;
608         }
609 
610         parent = node.parent;
611         // 保存"取代節點"的顏色
612         color = node.color;
613 
614         if (child!=null)
615             child.parent = parent;
616 
617         // "node節點"不是根節點
618         if (parent!=null) {
619             if (parent.left == node)
620                 parent.left = child;
621             else
622                 parent.right = child;
623         } else {
624             this.mRoot = child;
625         }
626 
627         if (color == BLACK)
628             removeFixUp(child, parent);
629         node = null;
630     }
631 
632     /* 
633      * 刪除結點(z),並返回被刪除的結點
634      *
635      * 參數說明:
636      *     tree 紅黑樹的根結點
637      *     z 刪除的結點
638      */
639     public void remove(T key) {
640         RBTNode<T> node; 
641 
642         if ((node = search(mRoot, key)) != null)
643             remove(node);
644     }
645 
646     /*
647      * 銷燬紅黑樹
648      */
649     private void destroy(RBTNode<T> tree) {
650         if (tree==null)
651             return ;
652 
653         if (tree.left != null)
654             destroy(tree.left);
655         if (tree.right != null)
656             destroy(tree.right);
657 
658         tree=null;
659     }
660 
661     public void clear() {
662         destroy(mRoot);
663         mRoot = null;
664     }
665 
666     /*
667      * 打印"紅黑樹"
668      *
669      * key        -- 節點的鍵值 
670      * direction  --  0,表示該節點是根節點;
671      *               -1,表示該節點是它的父結點的左孩子;
672      *                1,表示該節點是它的父結點的右孩子。
673      */
674     private void print(RBTNode<T> tree, T key, int direction) {
675 
676         if(tree != null) {
677 
678             if(direction==0)    // tree是根節點
679                 System.out.printf("%2d(B) is root\n", tree.key);
680             else                // tree是分支節點
681                 System.out.printf("%2d(%s) is %2d's %6s child\n", tree.key, isRed(tree)?"R":"B", key, direction==1?"right" : "left");
682 
683             print(tree.left, tree.key, -1);
684             print(tree.right,tree.key,  1);
685         }
686     }
687 
688     public void print() {
689         if (mRoot != null)
690             print(mRoot, mRoot.key, 0);
691     }
692 }
View Code

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

 1 /**
 2  * Java 語言: 二叉查找樹
 3  *
 4  * @author skywang
 5  * @date 2013/11/07
 6  */
 7 public class RBTreeTest {
 8 
 9     private static final int a[] = {10, 40, 30, 60, 90, 70, 20, 50, 80};
10     private static final boolean mDebugInsert = false;    // "插入"動做的檢測開關(false,關閉;true,打開)
11     private static final boolean mDebugDelete = false;    // "刪除"動做的檢測開關(false,關閉;true,打開)
12 
13     public static void main(String[] args) {
14         int i, ilen = a.length;
15         RBTree<Integer> tree=new RBTree<Integer>();
16 
17         System.out.printf("== 原始數據: ");
18         for(i=0; i<ilen; i++)
19             System.out.printf("%d ", a[i]);
20         System.out.printf("\n");
21 
22         for(i=0; i<ilen; i++) {
23             tree.insert(a[i]);
24             // 設置mDebugInsert=true,測試"添加函數"
25             if (mDebugInsert) {
26                 System.out.printf("== 添加節點: %d\n", a[i]);
27                 System.out.printf("== 樹的詳細信息: \n");
28                 tree.print();
29                 System.out.printf("\n");
30             }
31         }
32 
33         System.out.printf("== 前序遍歷: ");
34         tree.preOrder();
35 
36         System.out.printf("\n== 中序遍歷: ");
37         tree.inOrder();
38 
39         System.out.printf("\n== 後序遍歷: ");
40         tree.postOrder();
41         System.out.printf("\n");
42 
43         System.out.printf("== 最小值: %s\n", tree.minimum());
44         System.out.printf("== 最大值: %s\n", tree.maximum());
45         System.out.printf("== 樹的詳細信息: \n");
46         tree.print();
47         System.out.printf("\n");
48 
49         // 設置mDebugDelete=true,測試"刪除函數"
50         if (mDebugDelete) {
51             for(i=0; i<ilen; i++)
52             {
53                 tree.remove(a[i]);
54 
55                 System.out.printf("== 刪除節點: %d\n", a[i]);
56                 System.out.printf("== 樹的詳細信息: \n");
57                 tree.print();
58                 System.out.printf("\n");
59             }
60         }
61 
62         // 銷燬二叉樹
63         tree.clear();
64     }
65 }
View Code

 

紅黑樹的Java測試程序

前面已經給出了紅黑樹的測試代碼(RBTreeTest.java),這裏就再也不重複說明。下面是測試程序的運行結果:

== 原始數據: 10 40 30 60 90 70 20 50 80 
== 前序遍歷: 30 10 20 60 40 50 80 70 90 
== 中序遍歷: 10 20 30 40 50 60 70 80 90 
== 後序遍歷: 20 10 50 40 70 90 80 60 30 
== 最小值: 10
== 最大值: 90
== 樹的詳細信息: 
30(B) is root
10(B) is 30's   left child
20(R) is 10's  right child
60(R) is 30's  right child
40(B) is 60's   left child
50(R) is 40's  right child
80(B) is 60's  right child
70(R) is 80's   left child
90(R) is 80's  right child
相關文章
相關標籤/搜索