<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    上善若水
    In general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation. To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatra
    posts - 146,comments - 147,trackbacks - 0
    因為看EHCache中溢出文件的管理代碼,它用到了AA-Tree作為文件中的磁盤管理,因而決定先復習以下紅黑樹(RBT, Red Black Tree),順便看看TreeMap的代碼。關于紅黑樹,網上已經有各種相關的文章了,因而屬于多一篇不多,少一篇不少的狀態,因而我純粹當作是我自己理解的紀錄,加深印象,如果能對部分思路和我類似的人有一些幫助,那就最好了。基于這樣的目的,我并不打算深入,如果想看更深入的或者更好的,可以讀讀我最后的參考鏈接。

    紅黑樹引入的目的
    首先要從對有序序列的查找問題開始,對一個靜態的有序序列數組,只需要二分查找即可得到O(log(n))的查找效率;然而實現并不會顯得那么“靜態”,需要實現動態的插入、刪除同時保持序列的有序性,并且盡量提高插入、刪除、查找的效率。

    為實現動態插入、刪除,最簡單的實現是二叉排序樹(BST, Binary Sort Tree),它具有以下性質:
    1. 它可以是一個空樹。
    2. 若它的左子樹不為空,則它的左子樹所有節點的值均小于根節點的值。
    3. 若它的右子樹不為空,則它的右子樹所有節點的值均大于根節點的值。
    4. 它的左子樹和右子樹都是一個二叉排序樹。
    根據以上性質,
    對查找,比較查找數和當前節點,如果查找數和當前節點相等,則找到返回;如果查找數小于當前節點,查找其左子樹,如果查找數大于當前節點,查找其右子樹,直到找到或直到葉子節點為null,返回null。
    對插入,先查找這棵樹,如果找到已存在的節點,更新節點值,否則把新值插入到最后一個為null的節點中。
    對刪除,首先找到要刪除的節點,a)如果找到的節點P沒有子節點,直接刪除即可;b)如果找到的節點P只有左子樹或右子樹,刪除該節點,并將其父節點原來的指針指向它唯一的左子樹、或右子樹即可;c)如果找到的節點P既有左子樹又有右子樹,可以有兩種做法:I)刪除節點P,把節點P的父節點原來指向P的指針指向節點P的左字節點,而將節點P的右節點插入到節點P右節點的最右葉子節點上(如果節點P是其父節點的右節點,則將節點P的父節點原來指向P節點的指針指向P節點的右子樹,而將節點P的左子樹插入到節點P最左葉子節點上),II)將節點P替換成節點P的直接前驅(或直接后繼),然后刪除節點P的直接前驅(或直接后繼)(注:直接前驅查找:節點P左子樹的最右節點,直接后繼查找:節點P右子樹的最左節點)。
    二叉排序樹實現比較簡單,但是它查找效率卻不理想,最好的效率是O(log(n)),最會效率為O(n)。

    為了提高查找效率,后來有人提出了平衡二叉樹(AVL樹),它具有二叉排序樹的所有特點,并添加了以下性質:
    1. 左子樹和右子樹的深度之差絕對值不超過1。
    2. 左子樹和右子樹都是平衡二叉樹。
    為了實現平衡二叉樹,需要在沒個節點上添加平衡因子字段,在插入后,如果發現平衡因子不符合性質1,則需要經過旋轉以調整。平衡二叉樹可以保證其查找的最好、最壞查找時間復雜度都為O(log(n))。

    紅黑樹是平衡二叉樹的變種,它是一種自平衡的二叉排序樹,也需要通過一些旋轉調整以保持它的性質,它的名字是在 Leo J. Guibas 和 Robert Sedgewick 于1978年寫的一篇論文中獲得的。不同于平衡二叉樹的高度平衡,紅黑樹只能保證從根到葉子節點的最長路徑不超過最短路徑的2倍,因而它的最壞查找時間復雜度為O(2log(n + 1)),然而有研究表明它的統計性能要好于平衡二叉樹,因而它具有更廣泛的應用。

    紅黑樹的性質
    一棵樹需要滿足以下性質才能保證它是一顆紅黑樹:
    1. 它是一顆二叉查找樹(BST)。
    2. 它的所有節點是紅色或黑色。
    3. 它的根節點是黑色。
    4. 它的葉子節點是黑色(空節點視為葉子節點)。
    5. 它的紅節點的子節點必須是黑色的;而黑節點的字節點可以是黑色或紅色。
    6. 它任一節點到其后代的葉子節點路徑上有相同的黑色節點數。
    一般的文章都是把性質列出來,然后根據這些性質來寫代碼實現(我也一樣:)),但是如何得出這些性質呢?多問幾個為什么總是好事,這個問題需要去讀上面提到的論文,我沒讀過也不打算讀,這貌似不是我能涉及的,那就提出問題不回答了。。。

    TreeMap中紅黑樹的節點
    對一顆樹的節點,最基礎是該節點的值、左節點指針、右節點指針。對TreeMap,因為它存儲的是鍵值對,因而它包含了key、value,為了紀錄節點的顏色,它還需要有color字段:
        private static final boolean RED   = false;
        private static final boolean BLACK = true;

        static final class Entry<K,V> implements Map.Entry<K,V> {
            K key;
            V value;
            Entry<K,V> left = null;
            Entry<K,V> right = null;
            Entry<K,V> parent;
            boolean color = BLACK;
            ....
        }

    TreeMap中紅黑樹節點插入
    紅黑數的插入分以下步驟:
    1. 如果當前為空樹,插入節點直接作為根節點,并將該節點顏色比較
    2. 以二叉排序樹的查找算法查找當前樹,如果在當前樹中找到已存在的節點,更新節點的值,并返回。
    3. 否則,創建一個新節點,將其插入到最后一個查找到的葉子節點上,其初始顏色為紅色。
    4. 如果新插入節點的父節點是黑節點,則沒有破壞當前紅黑樹的性質,直接返回。
    5. 如果新插入節點的父節點是紅節點,則需要做一些調整。
    在TreeMap中,key值的比較可以通過構造TreeMap傳入的Comparator實例,如果沒有Comparator,則key必須實現Comparable接口作為比較邏輯,key不可以為null。以二叉排序樹的算法插入新節點的代碼比較簡單:
        public V put(K key, V value) {
            Entry<K,V> t = root;
            if (t == null) {
                compare(key, key); // type (and possibly null) check
                root = new Entry<>(key, value, null);
                size = 1;
                modCount++;
                return null;
            }
            int cmp;
            Entry<K,V> parent;
            // split comparator and comparable paths
            Comparator<? super K> cpr = comparator;
            if (cpr != null) {
                do {
                    parent = t;
                    cmp = cpr.compare(key, t.key);
                    if (cmp < 0)
                        t = t.left;
                    else if (cmp > 0)
                        t = t.right;
                    else
                        return t.setValue(value);
                } while (t != null);
            }
            else {
                if (key == null)
                    throw new NullPointerException();
                Comparable<? super K> k = (Comparable<? super K>) key;
                do {
                    parent = t;
                    cmp = k.compareTo(t.key);
                    if (cmp < 0)
                        t = t.left;
                    else if (cmp > 0)
                        t = t.right;
                    else
                        return t.setValue(value);
                } while (t != null);
            }
            Entry<K,V> e = new Entry<>(key, value, parent);
            if (cmp < 0)
                parent.left = e;
            else
                parent.right = e;
            fixAfterInsertion(e);
            size++;
            modCount++;
            return null;
        }
        private void fixAfterInsertion(Entry<K,V> x) {
            x.color = RED;
            ..
            root.color = BLACK;
        }

    TreeMap中紅黑樹新節點插入后調整

    紅黑樹的調整比較復雜,首先它會從當前節點向上查找,直到當前節點為null,或是root節點,或者當前節點的父節點顏色不是紅色,然后根據以下不同情況做處理(設當前節點為C(紅色),其父節點為P(紅色),其祖先節點為A(黑色),其叔叔節點為U(待定)):
    1. P是A的左子樹,U節點顏色為紅色,此時不管C是節點是P的左子樹還是右子樹,只需要將P和U設為黑色,A設為紅色,則可保證當前局部樹符合紅黑樹定義,把A作為新插入節點重新調整,如果當前樹已經是整棵樹,則因為根節點為紅色,不符合紅黑樹定義,此時只需要將根節點顏色設置為黑色即可,即fixAfterInsertion()最后一句代碼的作用。
    2. P是A的左子樹,U節點顏色為黑色,C是P的左子樹,將P設置為黑色,A設置為紅色,并對A做右旋操作。此時C的父節點已變為黑色,循環可以直接退出。
    3. P是A的左子樹,U節點顏色為黑色,C是P的右子樹,此時只需要先對P左旋,然后設置C為黑色,A為紅色,并對A右旋,此時P的父節點已變為黑色,循環可以直接退出。
    如下圖所示:

    代碼:
            while (x != null && x != root && x.parent.color == RED) {
                if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
                    Entry<K,V> y = rightOf(parentOf(parentOf(x)));
                    if (colorOf(y) == RED) {
                        setColor(parentOf(x), BLACK);
                        setColor(y, BLACK);
                        setColor(parentOf(parentOf(x)), RED);
                        x = parentOf(parentOf(x));
                    } else {
                        if (x == rightOf(parentOf(x))) {
                            x = parentOf(x);
                            rotateLeft(x);
                        }
                        setColor(parentOf(x), BLACK);
                        setColor(parentOf(parentOf(x)), RED);
                        rotateRight(parentOf(parentOf(x)));
                    }
                } else {
                    ....
                }
            }
    4. P是A的右子樹,U節點顏色為紅色,此時不管C是節點是P的左子樹還是右子樹,只需要將P和U設為黑色,A設為紅色,則可保證當前局部樹符合紅黑樹定義,把A作為新插入節點重新調整,如果當前樹已經是整棵樹,則因為根節點為紅色,不符合紅黑樹定義,此時只需要將根節點顏色設置為黑色即可,即fixAfterInsertion()最后一句代碼的作用。
    5. P是A的右子樹,U節點顏色為黑色,C是P的右子樹,將P設置為黑色,A設置為紅色,并對A做左旋操作。此時C的父節點以變為黑色,循環可以直接退出。
    6. P是A的右子樹,U節點顏色為黑色,C是P的左子樹,此時只需要先對P左旋,然后設置C為黑色,A為紅色,并對A右旋,此時P的父節點已為黑色,循環可以直接退出。
    如下圖所示:

    代碼:
            while (x != null && x != root && x.parent.color == RED) {
                if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
                    ....
                } else {
                    Entry<K,V> y = leftOf(parentOf(parentOf(x)));
                    if (colorOf(y) == RED) {
                        setColor(parentOf(x), BLACK);
                        setColor(y, BLACK);
                        setColor(parentOf(parentOf(x)), RED);
                        x = parentOf(parentOf(x));
                    } else {
                        if (x == leftOf(parentOf(x))) {
                            x = parentOf(x);
                            rotateRight(x);
                        }
                        setColor(parentOf(x), BLACK);
                        setColor(parentOf(parentOf(x)), RED);
                        rotateLeft(parentOf(parentOf(x)));
                    }
                }
            }

    TreeMap中紅黑樹節點刪除
    紅黑樹的刪除類似二叉查找樹刪除邏輯類似,在對同時有左子樹和右子樹存在時,TreeMap選擇先將要刪除的節點替換成其直接后繼節點,然后刪除其直接后繼節點(其直接后繼節點不可能同時存在左子節點和右子字節點)。對紅黑樹,由于紅色節點不影響路徑計算(性質6),因而對紅色節點可以直接刪除。然而在刪除黑色節點時,如果刪除的節點不是樹的唯一節點,那么在某些路徑上的黑色節點數會發生改變,破壞性質6;如果被刪除的唯一子節點為紅色,而父節點也為紅色,那么性質5被破壞,因為存在紅色節點的子節點為紅色;如果刪除的是根節點,而它的唯一子節點是紅色,則性質3被破壞。因而需要做一些調整。
        private void deleteEntry(Entry<K,V> p) {
            modCount++;
            size--;

            // If strictly internal, copy successor's element to p and then make p
            
    // point to successor.
            if (p.left != null && p.right != null) {
                Entry<K,V> s = successor(p);
                p.key = s.key;
                p.value = s.value;
                p = s;
            } // p has 2 children

            
    // Start fixup at replacement node, if it exists.
            Entry<K,V> replacement = (p.left != null ? p.left : p.right);

            if (replacement != null) {
                // Link replacement to parent
                replacement.parent = p.parent;
                if (p.parent == null)
                    root = replacement;
                else if (p == p.parent.left)
                    p.parent.left  = replacement;
                else
                    p.parent.right = replacement;

                // Null out links so they are OK to use by fixAfterDeletion.
                p.left = p.right = p.parent = null;

                // Fix replacement
                if (p.color == BLACK)
                    fixAfterDeletion(replacement);
            } else if (p.parent == null) { // return if we are the only node.
                root = null;
            } else { //  No children. Use self as phantom replacement and unlink.
                if (p.color == BLACK)
                    fixAfterDeletion(p);

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

    TreeMap中紅黑樹刪除黑色節點后調整
    調整的邏輯分以下步驟來考慮(假設新替換的節點為C,即代碼中的x參數,C的父節點為P,C是P的左子節點,C的兄弟節點為S,S的左子樹為SL,S的右子樹為SR):
    1. 如果C為紅色,直接將C標記為黑色即可,因為刪除的黑節點數被該節點補上,該樹已經恢復成一顆紅黑樹。
    2. 如果C為黑色,且C為根節點,直接返回。
    3. 如果C為黑色,且S為紅色,那么節點P、SL、SR都為黑色,此時設置P為紅色,S為黑色,對P左旋,并重新計算S,即S變為SL,即把問題轉換為兄弟節點為黑色的情況。圖片來自http://blog.csdn.net/v_JULY_v/article/details/6105630,自己畫太麻煩了,雖然圖片的命名規則和我的不一樣,湊合的看把,囧。
     
    4. 如果C為黑色,S為黑色,且SL、SR都為黑色,將S設置為紅色,P賦值給C,重新計算。

    5. 如果C為黑色,S為黑色,且SL為紅色,SR為黑色,那么設置SL為黑色,S為紅色,對S右旋,重新設置S為SL。

    6. 如果C為黑色,S為黑色,且SR為紅色,SL為任一顏色,則把S節點的顏色設置為P節點的顏色,設置P節點的顏色為黑色,SR節點的顏色為黑色,對P節點右旋,算法結束。
     
    當C為P的右子節點時,其邏輯和以上對稱,不再贅述。
        private void fixAfterDeletion(Entry<K,V> x) {
            while (x != root && colorOf(x) == BLACK) {
                if (x == leftOf(parentOf(x))) {
                    Entry<K,V> sib = rightOf(parentOf(x));

                    if (colorOf(sib) == RED) {
                        setColor(sib, BLACK);
                        setColor(parentOf(x), RED);
                        rotateLeft(parentOf(x));
                        sib = rightOf(parentOf(x));
                    }

                    if (colorOf(leftOf(sib))  == BLACK &&
                        colorOf(rightOf(sib)) == BLACK) {
                        setColor(sib, RED);
                        x = parentOf(x);
                    } else {
                        if (colorOf(rightOf(sib)) == BLACK) {
                            setColor(leftOf(sib), BLACK);
                            setColor(sib, RED);
                            rotateRight(sib);
                            sib = rightOf(parentOf(x));
                        }
                        setColor(sib, colorOf(parentOf(x)));
                        setColor(parentOf(x), BLACK);
                        setColor(rightOf(sib), BLACK);
                        rotateLeft(parentOf(x));
                        x = root;
                    }
                } else { // symmetric
                    Entry<K,V> sib = leftOf(parentOf(x));

                    if (colorOf(sib) == RED) {
                        setColor(sib, BLACK);
                        setColor(parentOf(x), RED);
                        rotateRight(parentOf(x));
                        sib = leftOf(parentOf(x));
                    }

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

            setColor(x, BLACK);
        }

    TreeMap中紅黑樹節點查找
    紅黑樹的節點查找同二叉查找樹邏輯,不再贅述。這里有一點不太明白:getEntryUsingComparator()方法注釋中說它從getEntry()方法提取出來是為了性能上的考慮,這是為什么?
        /**
         * Version of getEntry using comparator. Split off from getEntry
         * for performance. (This is not worth doing for most methods,
         * that are less dependent on comparator performance, but is
         * worthwhile here.)
         
    */
        final Entry<K,V> getEntryUsingComparator(Object key)

    TreeMap中其他方法
    TreeMap中其他方法實現比較直觀,只要理解了紅黑樹,基本上很容易理解,不再贅述。

    參考鏈接:
    http://blog.csdn.net/v_JULY_v/article/details/6105630
    http://blog.csdn.net/zhaojinjia/article/details/8120403
    http://blog.csdn.net/eric491179912/article/details/6179908
    http://dongxicheng.org/structure/red-black-tree/
    posted on 2013-10-26 18:41 DLevin 閱讀(2953) 評論(0)  編輯  收藏 所屬分類: Core Java
    主站蜘蛛池模板: 久久久久久国产精品免费无码| 国产V片在线播放免费无码| 鲁大师在线影院免费观看| 亚洲日韩在线中文字幕第一页| 免费国产va在线观看| 亚洲国产精品人人做人人爱| 免费无码午夜福利片69| 亚洲欧洲中文日韩久久AV乱码| 一个人免费观看视频在线中文| 亚洲国产精品狼友中文久久久| 无码AV动漫精品一区二区免费| 亚洲欧洲日产国码在线观看| 免费播放一区二区三区| 91嫩草亚洲精品| 国产美女精品视频免费观看| 日韩久久无码免费毛片软件| 亚洲色婷婷综合久久| 在线成人爽a毛片免费软件| 亚洲va成无码人在线观看| 日本视频免费在线| 人成午夜免费大片在线观看| 亚洲精品高清无码视频| 久久国产免费福利永久| 亚洲av成人无码网站…| 久久国产成人亚洲精品影院| 一级毛片在线观看免费| 亚洲日韩看片无码电影| 伊人久久精品亚洲午夜| 黄网站色在线视频免费观看| 国产青草亚洲香蕉精品久久| 亚洲精品制服丝袜四区| 在线v片免费观看视频| 日日摸夜夜添夜夜免费视频| 亚洲AV无码专区亚洲AV伊甸园| 波多野结衣免费在线| 丰满少妇作爱视频免费观看| 97se亚洲综合在线| 免费夜色污私人影院在线观看| 三年片在线观看免费观看大全动漫| 四虎必出精品亚洲高清| 国产av无码专区亚洲av桃花庵|