從TreeMap學習紅黑樹

紅黑樹是一種自平衡二叉查找樹,常用于鍵值對存儲,例如Java的TreeMap中就采用紅黑樹實現。它可以在O(logN)時間內查找、插入和刪除

紅黑樹定義與性質

紅黑樹定義

  1. 點是紅色或者黑色

  2. 根節點是黑色

  3. 所有葉子節點是黑色(null節點)

  4. 每個紅色節點都必須要有兩個黑色子節點

  5. 從任一節點到葉子節點都包含同樣數目的黑色節點

紅黑樹性質

根據紅黑樹的定義,從根到葉子的最長路徑不多于最短的兩倍。由于性質4每個紅色節點均有兩個黑色節點,性質5限制了黑色節點的數目,這樣可以限制最短的路徑為全是黑色節點的,而最長的路徑為紅黑節點交替的路徑

TreeMap中紅黑樹

紅黑樹仍是一個二叉排序樹,如果是二叉排序樹那么

  • 若左子樹不空,則左子樹上的值均小于它的根節點

  • 若右子樹不空,則右子樹上的值均大于它的根節點

  • 它的左右字數也分別為排序二叉樹

那么,對一棵二叉排序樹進行中序遍歷就可以得到排序后的結果

二叉排序樹.png

中序排序后為 1,2,3,4,5,7

樹的旋轉

紅黑樹的旋轉可以保持節點符合排序的規則,但是不一定能使其滿足紅黑樹的紅黑顏色規則,需要對其進行修復。其操作分為左旋,右旋

  • 左旋

操作在旋轉節點的右子樹,將待旋轉節點的右節點上升到父節點的位置上

左旋.png
private void rotateLeft(RedBlackNode<T> node) {
    if (node != null) {
        RedBlackNode<T> rChild = node.right;
        node.right = rChild.left;
        if (rChild.left != null)
            rChild.left.parent = node;
        rChild.parent = node.parent;
        if (node.parent == null)
            root = rChild;
        else if (node.parent.left == node)
            node.parent.left = rChild;
        else
            node.parent.right = rChild;
        rChild.left = node;
        node.parent = rChild;
    }
}
  • 右旋

操作在旋轉節點的左子樹,將待旋轉節點的左節點上升到父節點的位置上

右旋.png
private void rotateRight(RedBlackNode<T> node) {
    if (node != null) {
        RedBlackNode<T> lChild = node.left;
        node.left = lChild.right;
        if (lChild.right != null)
            lChild.parent = node;
        lChild.parent = node.parent;
        if (node.parent == null)
            root = lChild;
        else if (node.parent.left == node)
            node.parent.left = lChild;
        else
            node.parent.right = lChild;
        lChild.right = node;
        node.parent = lChild;
    }
}

紅黑樹的插入

紅黑樹的插入,首先確認插入的位置

private T insert(final RedBlackNode<T> newNode) {
    RedBlackNode<T> t = root;
    if (t == null) { // 如果為空樹
        root = newNode;
        size = 1;
        return newNode.key;
    }
    RedBlackNode<T> parent;
    int cmp;
    do {
        parent = t;
        cmp = compare(newNode, t);
        if (cmp < 0) {
            t = t.left;
        } else if (cmp > 0) {
            t = t.right;
        } else {
            return t.key;
        }
    } while (t != null); // 查找到合適的位置

    newNode.parent = parent;
    if (cmp < 0) {
        parent.left = newNode;
    } else {
        parent.right = newNode;
    }
    fixInsertion(newNode);
    size++;
    return null;
}

紅黑樹插入修復

插入之后需要對樹進行修復,使其滿足紅黑樹的性質

其中,

  • 如果插入的是根節點,將新加入節點涂黑,對樹沒有影響,直接插入

  • 如果插入的節點的父節點為黑色節點,對樹沒有影響

但是,當遇到

  1. 當前節點的父節點是紅色并且叔叔節點是紅色

插入新節點為顏色為紅色,不符合紅黑樹定義。需要進行調整

假如叔叔節點為祖父節點的右孩子。那么插入修復需要將當前節點的父親節點和叔叔節點的顏色改為黑色,并將祖父節點變更為紅色,當前節點指向祖父節點,繼續進行修復

修復前
修復后
  1. 當前節點的父節點為紅色,叔叔節點為黑色,當前節點為父節點的右節點

同樣不符合紅黑樹定義

如果當前節點的父節點為祖父節點的左孩子,則將當前節點指向當前節點的父節點,對新當前節點左旋

修復前
修復后

情況2修復完成之后,需要繼續進行情況3的修復

  1. 當前節點的父節點為紅色,叔叔節點為黑色,當前節點為父節點左節點

如果當前節點的父節點是祖父節點的左孩子,則將父節點變為黑色,祖父節點變為紅色,并且以祖父節點為支點右旋

修復前
修復后
private void fixInsertion(RedBlackNode<T> node) {
    node.color = RED;

    while (node != null && node != root && colorOf(parentOf(node)) == RED {
        if (parentOf(node) == leftOf(ancestorOf(node))) {
            // 當前的父節點是祖父節點的左孩子
            RedBlackNode<T> uncle = rightOf(ancestorOf(node));
            if (colorOf(uncle) == RED) {
                setColor(parentOf(node), BLACK);
                setColor(uncle, BLACK);
                setColor(ancestorOf(node), RED);
                node = ancestorOf(node);
            } else {
                if (node == rightOf(parentOf(node))) {
                    node = parentOf(node);
                    rotateLeft(node);
                }
                setColor(parentOf(node), BLACK);
                setColor(ancestorOf(node), RED);
                rotateRight(ancestorOf(node));
            }
        } else {
            RedBlackNode<T> uncle = leftOf(ancestorOf(node));
            if (colorOf(uncle) == RED) {
                setColor(parentOf(node), BLACK);
                setColor(uncle, BLACK);
                setColor(ancestorOf(node), RED);
                node = ancestorOf(node);
            } else {
                if (node == leftOf(parentOf(node))) {
                    node = parentOf(node);
                    rotateRight(node);
                }
                setColor(parentOf(node), BLACK);
                setColor(ancestorOf(node), RED);
                rotateLeft(ancestorOf(node));
            }
        }
    }
    root.color = BLACK;
}

插入的修復過程是不斷向走向根節點的,然后把整棵樹修復

紅黑樹的刪除

刪除紅黑樹中的節點之后,需要對樹進行維護使得紅黑樹仍符合紅黑樹的定義

  1. 如果被刪除的節點是葉結點,沒有孩子,直接從其父節點中刪除此節點即可

  2. 如果只有一個孩子,則直接將父節點的對應的孩子指向這個孩子即可

  3. 如果有兩個孩子,情況會復雜點。首先需要保證符合排序二叉樹的性質。刪除此結點之后,可以選擇其左子樹的最大結點或者右子樹的最小結點替換。TreeMap中是尋找了被刪除的結點的中序遍歷的后繼結點,也就是選擇了右子樹的最小結點來替換這個結點。

private void deleteNode(RedBlackNode<T> node) {
    size--;
    if (node.left != null && node.right != null) {
        // 中序后繼節點替代
        RedBlackNode<T> next = successor(node);
        // 注意此時替換了key值
        node.key = next.key;
        // 僅僅替換了引用
        node = next;
    }
    // 對替換的結點 與 被刪除的結點進行替換
    RedBlackNode<T> replacement = (node.left != null ? node.left : node.right);

    // 對替換的結點的孩子進行連接,并脫離被替換節點
    if (replacement != null) {
        replacement.parent = node.parent;
        if (node.parent == null)
            root = replacement;
        else if (node == node.parent.left)
            node.parent.left = replacement;
        else node.parent.right = replacement;

        node.left = node.right = node.parent = null;

        if (node.color == BLACK)
            fixDeletion(replacement);
    } else if (node.parent == null) {
        // 當前刪除的節點是根節點
        root = null;
    } else {
        if (node.color == BLACK)
            fixDeletion(node);

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

紅黑樹刪除修復

如果被刪除的結點是紅色,則不需要做任何修復即可

如果被是刪除結點是黑色,則有可能會造成紅黑樹性質被破壞

  • 如果刪除的黑色不是紅黑樹的唯一結點,那么從被刪除結點的分支的黑色結點數必然會不正確,性質5被破壞
  • 如果被刪除結點的唯一非空子結點是紅色,且父結點也是紅色,違反性質4
  • 如果被刪除結點為根節點,且其替換結點為紅色,違反性質2

針對以上的情況,分以下解決(討論當前結點為父結點左孩子)

"下面我們用一個分析技巧:我們從被刪結點后來頂替它的那個結點開始調整,并認為它有額外的一重黑色。這里額外一重黑色是什么意思呢,我們不是把紅黑樹的結點加上除紅與黑的另一種顏色,這里只是一種假設,我們認為我們當前指向它,因此空有額外一種黑色,可以認為它的黑色是從它的父結點被刪除后繼承給它的,它現在可以容納兩種顏色,如果它原來是紅色,那么現在是紅+黑,如果原來是黑色,那么它現在的顏色是黑+黑。有了這重額外的黑色,原紅黑樹性質5就能保持不變?,F在只要恢復其它性質就可以了,做法還是盡量向根移動和窮舉所有可能性。"--saturnman

  1. 當前結點(被替換的結點)為黑色+黑色,且兄弟結點為紅色

此時父結點為黑色,并且兄弟的孩子結點也都為黑色,那么把父結點染紅,兄弟結點染黑,之后以父結點為點左旋,更新下兄弟節點。這樣轉化問題為兄弟節點為黑色的問題。這時替換結點上仍有一重黑色,繼續進入算法

修復前
修復后
if (colorOf(sibling) == RED) {
    setColor(sibling, BLACK);
    setColor(parentOf(node), RED);
    rotateLeft(parentOf(node));
    sibling = rightOf(parentOf(node));
}
  1. 當前為黑+黑,兄弟節點為黑色,且兄弟節點的孩子也為黑色

這時候需要將當前結點和兄弟節點中剝離出來一層黑色給他們的父結點。那么當前為黑+黑,因此還是黑色,而兄弟結點只有一層黑色,因此變為紅色,如果父結點為紅色,則算法結束,否則繼續以父結點為當前結點繼續進行算法

修復前

修復后
if (colorOf(leftOf(sibling)) == BLACK &&
        colorOf(rightOf(sibling)) == BLACK) {
    setColor(sibling, RED);
    node = parentOf(node);
}
  1. 當前為黑+黑,兄弟結點為黑色,且兄弟結點的左孩子為紅色,右孩子為黑色

這種情況需要轉化為情況4,因此將兄弟結點染紅,兄弟的左子染黑,之后右旋,更新兄弟結點

修復前
修復后
if (colorOf(rightOf(sibling)) == BLACK) {
    setColor(leftOf(sibling), RED);
    setColor(sibling, RED);
    rotateRight(sibling);
    sibling = rightOf(parentOf(node));
}
  1. 當前結點為黑+黑,兄弟結點為黑色,且兄弟結點的右孩子為紅色,左孩子為任意顏色

將兄弟結點染成父結點顏色,父結點染成黑色,兄弟結點右孩子染黑,以父結點為支點左旋。當前結點設為根節點,調整結束

修復前
修復后
setColor(sibling, colorOf(parentOf(node)));
setColor(parentOf(node), BLACK);
setColor(rightOf(sibling), BLACK);
rotateLeft(parentOf(node));
node = root;

其實這個修復刪除的過程就是調整替代結點的多加的這層黑色,使其能夠補償到被刪除的黑色結點,這樣就可以在保持紅黑樹的性質。

參考

實現

package me.learn.datestruct;

import java.util.LinkedList;
import java.util.Queue;

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

    private transient RedBlackNode<T> root;
    private transient int size = 0;

    public void add(T key) {
        RedBlackNode<T> node = new RedBlackNode<>(key);
        insert(node);
    }

    public void remove(T key) {
        RedBlackNode<T> node = getNode(key);
        if (node == null)
            return;
        deleteNode(node);
    }

    private void deleteNode(RedBlackNode<T> node) {
        size--;
        if (node.left != null && node.right != null) {
            // 中序后繼節點替代
            RedBlackNode<T> next = successor(node);
            node.key = next.key;
            node = next;
        }
        RedBlackNode<T> replacement = (node.left != null ? node.left : node.right);

        if (replacement != null) {
            replacement.parent = node.parent;
            if (node.parent == null)
                root = replacement;
            else if (node == node.parent.left)
                node.parent.left = replacement;
            else node.parent.right = replacement;

            node.left = node.right = node.parent = null;

            if (node.color == BLACK)
                fixDeletion(replacement);
        } else if (node.parent == null) {
            // 當前刪除的節點是根節點
            root = null;
        } else {
            if (node.color == BLACK)
                fixDeletion(node);

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

    private void fixDeletion(RedBlackNode<T> node) {
        while (node != root && colorOf(node) == BLACK) {
            if (node == leftOf(parentOf(node))) {
                RedBlackNode<T> sibling = rightOf(parentOf(node));

                if (colorOf(sibling) == RED) {
                    setColor(sibling, BLACK);
                    setColor(parentOf(node), RED);
                    rotateLeft(parentOf(node));
                    sibling = rightOf(parentOf(node));
                }

                if (colorOf(leftOf(sibling)) == BLACK &&
                        colorOf(rightOf(sibling)) == BLACK) {
                    setColor(sibling, RED);
                    node = parentOf(node);
                } else {
                    if (colorOf(rightOf(sibling)) == BLACK) {
                        setColor(leftOf(sibling), RED);
                        setColor(sibling, RED);
                        rotateRight(sibling);
                        sibling = rightOf(parentOf(node));
                    }
                    setColor(sibling, colorOf(parentOf(node)));
                    setColor(parentOf(node), BLACK);
                    setColor(rightOf(sibling), BLACK);
                    rotateLeft(parentOf(node));
                    node = root;
                }
            } else {
                RedBlackNode<T> sibling = leftOf(parentOf(node));

                if (colorOf(sibling) == RED) {
                    setColor(sibling, BLACK);
                    setColor(parentOf(node), RED);
                    rotateRight(parentOf(node));
                    sibling = leftOf(parentOf(node));
                }

                if (colorOf(rightOf(sibling)) == BLACK &&
                        colorOf(leftOf(sibling)) == BLACK) {
                    setColor(sibling, RED);
                    node = parentOf(node);
                } else {
                    if (colorOf(leftOf(sibling)) == BLACK) {
                        setColor(rightOf(sibling), RED);
                        setColor(sibling, RED);
                        rotateLeft(sibling);
                        sibling = leftOf(parentOf(node));
                    }
                    setColor(sibling, colorOf(parentOf(node)));
                    setColor(parentOf(node), BLACK);
                    setColor(leftOf(sibling), BLACK);
                    rotateRight(parentOf(node));
                    node = root;
                }
            }
        }

        setColor(node, BLACK);
    }

    /**
     * node 中序的后繼節點
     * @param node
     * @return
     */
    private RedBlackNode<T> successor(RedBlackNode<T> node) {
        if (node == null)
            return null;
        if (node.right != null) {
            RedBlackNode<T> t = node.right;
            while (t.left != null)
                t = t.left;
            return t;
        } else {
            RedBlackNode<T> p = node.parent;
            RedBlackNode<T> child = node;
            while (p != null && child == p.right) {
                child = p;
                p = p.parent;
            }
            return p;
        }
    }

    private RedBlackNode<T> getNode(T key) {
        if (key == null)
            throw new NullPointerException();
        RedBlackNode<T> node = root;
        while (node != null) {
            int cmp = key.compareTo(node.key);
            if (cmp < 0)
                node = node.left;
            else if (cmp > 0)
                node = node.right;
            else
                return node;
        }
        return null;
    }

    private T insert(final RedBlackNode<T> newNode) {
        RedBlackNode<T> t = root;
        if (t == null) {
            root = newNode;
            size = 1;
            return newNode.key;
        }
        RedBlackNode<T> parent;
        int cmp;
        do {
            parent = t;
            cmp = compare(newNode, t);
            if (cmp < 0) {
                t = t.left;
            } else if (cmp > 0) {
                t = t.right;
            } else {
                return t.key;
            }
        } while (t != null);

        newNode.parent = parent;
        if (cmp < 0) {
            parent.left = newNode;
        } else {
            parent.right = newNode;
        }
        fixInsertion(newNode);
        size++;
        return null;
    }

    private void fixInsertion(RedBlackNode<T> node) {
        node.color = RED;

        while (node != null && node != root && colorOf(parentOf(node)) == RED) {
            if (parentOf(node) == leftOf(ancestorOf(node))) {
                // 當前的父節點是祖父節點的左孩子
                RedBlackNode<T> uncle = rightOf(ancestorOf(node));
                if (colorOf(uncle) == RED) {
                    setColor(parentOf(node), BLACK);
                    setColor(uncle, BLACK);
                    setColor(ancestorOf(node), RED);
                    node = ancestorOf(node);
                } else {
                    if (node == rightOf(parentOf(node))) {
                        node = parentOf(node);
                        rotateLeft(node);
                    }
                    setColor(parentOf(node), BLACK);
                    setColor(ancestorOf(node), RED);
                    rotateRight(ancestorOf(node));
                }
            } else {
                RedBlackNode<T> uncle = leftOf(ancestorOf(node));
                if (colorOf(uncle) == RED) {
                    setColor(parentOf(node), BLACK);
                    setColor(uncle, BLACK);
                    setColor(ancestorOf(node), RED);
                    node = ancestorOf(node);
                } else {
                    if (node == leftOf(parentOf(node))) {
                        node = parentOf(node);
                        rotateRight(node);
                    }
                    setColor(parentOf(node), BLACK);
                    setColor(ancestorOf(node), RED);
                    rotateLeft(ancestorOf(node));
                }
            }
        }
        root.color = BLACK;
    }

    private void rotateLeft(RedBlackNode<T> node) {
        if (node != null) {
            RedBlackNode<T> rChild = node.right;
            node.right = rChild.left;
            if (rChild.left != null)
                rChild.left.parent = node;
            rChild.parent = node.parent;
            if (node.parent == null)
                root = rChild;
            else if (node.parent.left == node)
                node.parent.left = rChild;
            else
                node.parent.right = rChild;
            rChild.left = node;
            node.parent = rChild;
        }
    }

    private void rotateRight(RedBlackNode<T> node) {
        if (node != null) {
            RedBlackNode<T> lChild = node.left;
            node.left = lChild.right;
            if (lChild.right != null)
                lChild.parent = node;
            lChild.parent = node.parent;
            if (node.parent == null)
                root = lChild;
            else if (node.parent.left == node)
                node.parent.left = lChild;
            else
                node.parent.right = lChild;
            lChild.right = node;
            node.parent = lChild;
        }
    }

    private void setColor(RedBlackNode<T> node, boolean color) {
        if (node != null) node.color = color;
    }

    private boolean colorOf(RedBlackNode<T> node) {
        return node == null ? BLACK : node.color;
    }

    private RedBlackNode<T> ancestorOf(RedBlackNode<T> node) {
        return parentOf(parentOf(node));
    }

    private RedBlackNode<T> parentOf(RedBlackNode<T> node) {
        return node.parent;
    }

    private RedBlackNode<T> leftOf(RedBlackNode<T> node) {
        return node.left;
    }

    private RedBlackNode<T> rightOf(RedBlackNode<T> node) {
        return node.right;
    }

    private int compare(RedBlackNode<T> t1, RedBlackNode<T> t2) {
        return t1.key.compareTo(t2.key);
    }

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

    static final class RedBlackNode<T extends Comparable<T>> {
        T key;
        RedBlackNode<T> left;
        RedBlackNode<T> right;
        RedBlackNode<T> parent;
        boolean color = BLACK;

        public RedBlackNode() {
        }

        public RedBlackNode(T key) {
            this.key = key;
        }

        public RedBlackNode(T key, RedBlackNode<T> parent) {
            this.key = key;
            this.parent = parent;
        }

        @Override
        public String toString() {
            String result = "";
            return color == RED && parent != null ?
                    String.format("[Red][parent[%s]][key[%s]]", parent.key, key)
                    : color == BLACK && parent != null ?
                    String.format("[BLK][parent[%s]][key[%s]]", parent.key, key)
                    : String.format("ROOT[key[%s]]", key);
        }
    }

    private void printTree() {
        RedBlackNode<T> rootData = root;
        Queue<RedBlackNode<T>> queue = new LinkedList<>();
        Queue<RedBlackNode<T>> queue1 = new LinkedList<>();
        queue.offer(rootData);
        boolean isDefaultQueue = true;

        while (!queue.isEmpty() || !queue1.isEmpty()) {
            Queue<RedBlackNode<T>> mQueue = isDefaultQueue ? queue : queue1;
            RedBlackNode<T> node = mQueue.poll();
            if (node != null) {
                String nodeInfo = "";
                if (node.parent != null) {
                    if (node == node.parent.left)
                        nodeInfo = "[LE]";
                    else
                        nodeInfo = "[RH]";
                }
                if (node.left != null) {
                    (isDefaultQueue ? queue1 : queue).offer(node.left);
                }
                if (node.right != null) {
                    (isDefaultQueue ? queue1 : queue).offer(node.right);
                }
                System.out.print(nodeInfo + node + "\t");
            } else {
                System.out.println();
                isDefaultQueue = !isDefaultQueue;
            }
        }
    }

    public static void main(String[] argv) {
        RedBlackTree<Integer> tree = new RedBlackTree<>();
        tree.add(1);
        tree.add(2);
        tree.add(3);
        tree.add(4);
        tree.add(5);
        tree.add(10);
        tree.printTree();
        System.out.println("\n===============================");
        tree.remove(4);
        tree.printTree();
    }
}

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,978評論 2 374

推薦閱讀更多精彩內容

  • 0.目錄 1.算法導論的紅黑樹本質上是2-3-4樹 2.紅黑樹的結構和性質 3.紅黑樹的插入 4.紅黑樹的刪除 5...
    王偵閱讀 2,508評論 1 2
  • 數據結構與算法--從平衡二叉樹(AVL)到紅黑樹 上節學習了二叉查找樹。算法的性能取決于樹的形狀,而樹的形狀取決于...
    sunhaiyu閱讀 7,674評論 4 32
  • 一. 算法之變,結構為宗 計算機在很多情況下被應用于檢索數據,比如航空和鐵路運輸業的航班信息和列車時刻表的查詢,都...
    Leesper閱讀 6,971評論 13 42
  • 1、紅黑樹介紹 紅黑樹又稱R-B Tree,全稱是Red-Black Tree,它是一種特殊的二叉查找樹,紅黑樹的...
    文哥的學習日記閱讀 9,897評論 1 35
  • R-B Tree簡介 R-B Tree,全稱是Red-Black Tree,又稱為“紅黑樹”,它一種特殊的二叉查找...
    張晨輝Allen閱讀 9,314評論 5 30