【集合系列】- 紅黑樹實現分析

1、故事的原由

JDK1.8最重要的就是引入了紅黑樹的設計(當衝突的鏈表長度超過8個的時候),爲何要這樣設計呢?好處就是避免在最極端的狀況下衝突鏈表變得很長很長,在查詢的時候,效率會很是慢。性能

  • 紅黑樹查詢:其訪問性能近似於折半查找,時間複雜度O(logn);
  • 鏈表查詢:這種狀況下,須要遍歷所有元素才行,時間複雜度O(n);

本文主要是講解紅黑樹的實現,只有充分理解了紅黑樹,對於後面的分析纔會更加順利。設計

簡單的說,紅黑樹是一種近似平衡的二叉查找樹,其主要的優勢就是「平衡「,即左右子樹高度幾乎一致,以此來防止樹退化爲鏈表,經過這種方式來保障查找的時間複雜度爲log(n)。3d

關於紅黑樹的內容,網上給出的內容很是多,主要有如下幾個特性:指針

  • 一、每一個節點要麼是紅色,要麼是黑色,但根節點永遠是黑色的;
  • 二、每一個紅色節點的兩個子節點必定都是黑色;
  • 三、紅色節點不能連續(也便是,紅色節點的孩子和父親都不能是紅色);
  • 四、從任一節點到其子樹中每一個葉子節點的路徑都包含相同數量的黑色節點;
  • 五、全部的葉節點都是是黑色的(注意這裏說葉子節點實際上是上圖中的 NIL 節點);

在樹的結構發生改變時(插入或者刪除操做),每每會破壞上述條件3或條件4,須要經過調整使得查找樹從新知足紅黑樹的條件。code

2、調整方式

上面已經說到當樹的結構發生改變時,紅黑樹的條件可能被破壞,須要經過調整使得查找樹從新知足紅黑樹的條件。blog

調整能夠分爲兩類:一類是顏色調整,即改變某個節點的顏色,這種比較簡單,直接將節點顏色進行轉換便可;另外一類是結構調整,改變檢索樹的結構關係。結構調整主要包含兩個基本操做:左旋(Rotate Left)右旋(RotateRight)get

2.一、左旋

左旋的過程是將p的右子樹繞p逆時針旋轉,使得p的右子樹成爲p的父親,同時修改相關節點的引用,使左子樹的深度加1,右子樹的深度減1,經過這種作法來調整樹的穩定性。過程以下:源碼

以jdk1.8爲例,打開HashMap的源碼部分,紅黑樹內部類TreeNode屬性分析:class

static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
        //指向父節點的指針
        TreeNode<K,V> parent;
        //指向左孩子的指針
        TreeNode<K,V> left;
        //指向右孩子的指針
        TreeNode<K,V> right;
        //前驅指針,跟next屬性相反的指向
        TreeNode<K,V> prev;
        //是否爲紅色節點
        boolean red;
        ......
}

左旋方法rotateLeft以下:效率

/*
 * 左旋邏輯
 */
static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
                                              TreeNode<K,V> p) {
            //root:表示根節點
            //p:表示要調整的節點
            //r:表示p的右節點
            //pp:表示p的parent節點
            //rl:表示p的右孩子的左孩子節點
            TreeNode<K,V> r, pp, rl;
            //r判斷,若是r爲空則旋轉沒有意義
            if (p != null && (r = p.right) != null) {
                //多個等號的鏈接操做從右往左看,設置rl的父親爲p
                if ((rl = p.right = r.left) != null)
                    rl.parent = p;
                //判斷p的父親,爲空,爲根節點,根節點的話就設置爲黑色
                if ((pp = r.parent = p.parent) == null)
                    (root = r).red = false;
                //判斷p節點是左兒子仍是右兒子
                else if (pp.left == p)
                    pp.left = r;
                else
                    pp.right = r;
                r.left = p;
                p.parent = r;
            }
            return root;
}

2.二、右旋

瞭解了左旋轉以後,相應的就會有右旋,邏輯基本也是同樣,只是方向變了。右旋的過程是將p的左子樹繞p順時針旋轉,使得p的左子樹成爲p的父親,同時修改相關節點的引用,使右子樹的深度加1,左子樹的深度減1,經過這種作法來調整樹的穩定性。實現過程以下:

一樣的,右旋方法rotateRight以下:

/*
 * 右旋邏輯
 */
static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,
                                               TreeNode<K,V> p) {
            //root:表示根節點
            //p:表示要調整的節點
            //l:表示p的左節點
            //pp:表示p的parent節點
            //lr:表示p的左孩子的右孩子節點
            TreeNode<K,V> l, pp, lr;
            //l判斷,若是l爲空則旋轉沒有意義
            if (p != null && (l = p.left) != null) {
                //多個等號的鏈接操做從右往左看,設置lr的父親爲p
                if ((lr = p.left = l.right) != null)
                    lr.parent = p;
                //判斷p的父親,爲空,爲根節點,根節點的話就設置爲黑色
                if ((pp = l.parent = p.parent) == null)
                    (root = l).red = false;
                //判斷p節點是右兒子仍是左兒子
                else if (pp.right == p)
                    pp.right = l;
                else
                    pp.left = l;
                l.right = p;
                p.parent = l;
            }
            return root;
}

3、操做示例介紹

3.一、插入調整過程圖解

3.二、刪除調整過程圖解

3.三、查詢過程圖解

4、總結

至此,紅黑樹的實現就基本完成了,關於紅黑樹的結構,有不少種狀況,狀況也比較複雜,可是總體調整流程,基本都是先調整結構而後調整顏色,直到最後知足紅黑樹特性要求爲止。整篇文章,若是有理解不當之處,歡迎指正!

5、參考

一、簡書 - JDK1.8紅黑樹實現分析
二、知乎 - 史上最清晰的紅黑樹講解

做者:炸雞可樂
出處:www.pzblog.cn

相關文章
相關標籤/搜索