一天一個算法——>紅黑樹JAVA實現

代碼均爲自己的思路,手動敲寫,如有bug,或者思路錯誤,歡迎指正,多多交流。

package tree;

/**
 * 紅黑樹(R-B Tree)
 * 遞歸方式空間複雜度爲O(log n),且受棧內存限制,故能使用循環的儘量使用循環,本例子使用while循環
 * 這裏只模擬int類型實現,如果需要其他類型,請將int類型修改爲泛型,並實現extends Comparable<T>接口,方便比較compareTo
 * 動態模擬實現:https://www.cs.usfca.edu/~galles/visualization/RedBlack.html
 * GitHub地址:https://github.com/hack-feng/algorithm
 * 理論知識:https://blog.csdn.net/qq_34988304/article/details/100135759
 * CSDN博客地址:
 * 聯繫作者:[email protected]
 *
 * 紅黑樹的特性:
 * (1)每個節點或者是黑色,或者是紅色。
 * (2)根節點是黑色。
 * (3)每個葉子節點(NIL)是黑色。 [注意:這裏葉子節點,是指爲空(NIL或NULL)的葉子節點!]
 * (4)如果一個節點是紅色的,則它的子節點必須是黑色的。
 * (5)從一個節點到該節點的子孫節點的所有路徑上包含相同數目的黑節點。
 */
public class RedBlackTree {

    private RBNode root;    // 根結點

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

    static class RBNode{
        // 節點顏色, 紅:false   黑:true
        boolean color;
        int data;
        RBNode left;
        RBNode right;
        RBNode parent;

        RBNode(int data, RBNode left, RBNode right){
            this.data = data;
            this.left = left;
            this.right = right;
        }
    }

    private void insert(int data){
        RBNode node = new RBNode(data, null, null);
        insert(node);
    }

    private void insert(RBNode node){
        // 定義node的根節點
        RBNode pNode = null;
        // 定義一個臨時節點
        RBNode tempNode = this.root;

        // 取出root的根節點
        while(tempNode != null){
            pNode = tempNode;
            if(tempNode.data > node.data){
                tempNode = tempNode.left;
            }else{
                tempNode = tempNode.right;
            }
        }

        // 設置node的父節點
        node.parent = pNode;

        // 判斷將node放在其父節點的左節點還是右節點
        if(pNode != null){
            if(pNode.data > node.data){
                pNode.left = node;
            }else{
                pNode.right = node;
            }
        }else{
            this.root = node;
        }

        // 初始化節點的顏色爲紅色
        node.color = RED;

        // 修正爲紅黑樹
        insertFixUp(node);
    }

    /**
     * 修正紅黑樹
     * 分爲一下幾種情況:
     * 1:如果是父節點,直接變成黑色
     * 2:如果父節點是黑色,不違背特性,直接添加即可
     * 3:如果父節點是紅色,添加違背特性。則又分爲一下幾種情況:
     *  3.1:父節點是祖父節點的左節點
     *      3.1.1:如果叔叔節點爲黑的時候
     *          3.1.1.1:如果插入的節點是父節點的左節點
     *              父節點變色,祖父節點變色,祖父節點右旋
     *          3.1.1.2:如果插入的節點是父節點的右節點
     *              父節點左旋,然後父節點變色,祖父節點變色,祖父節點右旋
     *      3.1.2:如果叔叔節點爲紅的時候
     *          直接將父節點和叔叔節點塗黑,祖父節點塗紅就可以了
     *  3.2:父節點是祖父節點的右節點
     *      3.2.1:如果叔叔節點爲黑的時候
     *          3.2.1.1:如果插入的節點是父節點的左節點
     *              父節點變色,祖父節點變色,祖父節點左旋
     *          3.2.1.2:如果插入的節點是父節點的右節點
     *              父節點右旋,然後父節點變色,祖父節點變色,祖父節點左旋
     *      3.2.2:如果叔叔節點爲紅的時候
     *          直接將父節點和叔叔節點塗黑,祖父節點塗紅就可以了
     */
    private void insertFixUp(RBNode node) {
        RBNode pNode, gNode;

        // 如果父節點不爲空,並且父節點的顏色是紅色,則會觸發樹旋轉
        while((pNode = node.parent) != null && isRed(pNode.color)){
            gNode = pNode.parent;

            // 父節點爲祖父節點的左節點時
            if(gNode.left == pNode){

                RBNode uNode = gNode.right;
                //叔叔節點爲紅色
                if(uNode != null && isRed(uNode.color)){
                    pNode.color = BLACK;
                    uNode.color = BLACK;
                    gNode.color = RED;
                    node = gNode;
                }else{
                    // 叔叔節點爲黑色,當前節點在父節點的右節點
                    if(pNode.right == node){
                        // 先把父節點左旋
                        leftRotate(pNode);
                    }
                    // 叔叔節點爲黑色,當前節點在父節點的左節點
                    pNode.color = BLACK;
                    gNode.color = RED;
                    rightRotate(gNode);
                }
            }else{
                RBNode uNode = gNode.left;
                //叔叔節點爲紅色
                if(uNode != null && isRed(uNode.color)){
                    pNode.color = BLACK;
                    uNode.color = BLACK;
                    gNode.color = RED;
                    node = gNode;
                }else{
                    // 叔叔節點爲黑色,當前節點在父節點的左節點
                    if(pNode.left == node){
                        // 先把父節點右旋
                        rightRotate(pNode);
                    }
                    // 叔叔節點爲黑色,當前節點在父節點的右節點
                    pNode.color = BLACK;
                    gNode.color = RED;
                    leftRotate(gNode);
                }
            }

        }

        // 將根節點設爲黑色
        this.root.color = BLACK;
    }

    // 右旋
    private void rightRotate(RBNode node){
        // 取出當前節點的左節點始終,賦值給leftNode
        RBNode leftNode = node.left;
        // 將leftNode的右節點賦值給node的左節點
        node.left = leftNode.right;

        // 因爲下面leftNode.right = node, 所以如果leftNode.right != null,修改leftNode.right.parent = node
        if(leftNode.right != null){
            leftNode.right.parent = node;
        }

        // 設置leftNode.parent
        leftNode.parent = node.parent;
        if(node.parent == null){
            // 如果node.parent爲null,說明node原來爲根節點,則將leftNode設爲新的根節點
            this.root = leftNode;
        }else{
            // 將指向node的父節點更改爲leftNode
            if(node.parent.right == node){
                node.parent.right = leftNode;
            }else{
                node.parent.left = leftNode;
            }
        }
        // 設置leftNode.right
        leftNode.right = node;
        // 設置node.parent
        node.parent = leftNode;
    }

    // 左旋,解釋同右旋
    private void leftRotate(RBNode node){

        RBNode rightNode = node.right;
        node.right = rightNode.left;

        if(rightNode.left != null){
            rightNode.left.parent = node;
        }

        rightNode.parent = node.parent;

        if(node.parent == null){
            this.root = rightNode;
        }else{
            if(node.parent.left == node){
                node.parent.left = rightNode;
            }else{
                node.parent.right = rightNode;
            }
        }

        rightNode.left = node;
        node.parent = rightNode;
    }

    private boolean isRed(boolean color){
        return !color;
    }

    public static void main(String[] args){
        RedBlackTree rbTree = new RedBlackTree();
//        int[] dataArray = new int[]{1, 2, 3, 4, 5, 6, 7, 8};
//        int[] dataArray = new int[]{8, 7, 6, 5, 4, 3, 2, 1};
        // 測試
        int[] dataArray = new int[]{3, 5, 8, 4, 7, 1, 6, 2};
        for (int i : dataArray) {
            rbTree.insert(i);
        }
        System.out.println(123);
    }
}

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