TreeMap之元素刪除

微信公衆號:I am CR7
若有問題或建議,請在下方留言;
最近更新:2018-09-05java

TreeMap之插入

     經過上一篇文章,介紹了二分查找樹的缺陷,引出了紅黑樹的介紹。進一步分析TreeMap中插入元素的源碼,最後藉助示例來加深對於紅黑樹的理解。詳細請看TreeMap之元素插入node

TreeMap之刪除

一、刪除指定key對應的元素:

 1public V remove(Object key) {
2    //根據key獲取鍵值對
3    Entry<K,V> p = getEntry(key);
4    if (p == null)
5        return null;
6    //刪除該鍵值對,並返回value值
7    V oldValue = p.value;
8    deleteEntry(p);
9    return oldValue;
10}
複製代碼

二、刪除節點,並對樹進行平衡調整:

2.一、源碼:
 1private void deleteEntry(Entry<K,V> p) {
2    modCount++;
3    size--;
4
5    //若是存在左右節點,則獲取其後繼節點,完成key、value的複製,p指向後繼節點
6    //此處思想就是將有左右節點的節點p的刪除轉化爲其後繼節點的刪除
7    if (p.left != null && p.right != null) {
8        Entry<K,V> s = successor(p);
9        p.key = s.key;
10        p.value = s.value;
11        p = s;
12    } // p has 2 children
13
14    //獲取刪除節點的替代節點
15    Entry<K,V> replacement = (p.left != null ? p.left : p.right);
16
17    //若是存在替代節點,則進行替代。接着先刪除節點p,再進行刪除的平衡調整
18    if (replacement != null) {
19        //用替代節點替代刪除節點
20        replacement.parent = p.parent;
21        if (p.parent == null)
22            root = replacement;
23        else if (p == p.parent.left)
24            p.parent.left  = replacement;
25        else
26            p.parent.right = replacement;
27
28        //刪除節點p
29        p.left = p.right = p.parent = null;
30
31        //若是刪除節點爲黑色,必然影響樹的平衡,進行平衡調整
32        if (p.color == BLACK)
33            fixAfterDeletion(replacement);
34    } else if (p.parent == null) { // return if we are the only node.
35        root = null;
36    } else {  //若是不存在替代節點,不須要替代。接着先進行刪除的平衡調整,再刪除節點p
37        //若是刪除節點爲黑色,必然影響樹的平衡,進行平衡調整
38        if (p.color == BLACK)
39            fixAfterDeletion(p);
40
41        //刪除節點p
42        if (p.parent != null) {
43            if (p == p.parent.left)
44                p.parent.left = null;
45            else if (p == p.parent.right)
46                p.parent.right = null;
47            p.parent = null;
48        }
49    }
50}
51
52//注意該方法執行的前提是t節點有左右節點,因此返回的是右子樹裏最左邊的非空
53static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) {
54    if (t == null)
55        return null;
56    else if (t.right != null) {  //存在右子樹
57        Entry<K,V> p = t.right;
58        while (p.left != null//一直往左走
59            p = p.left;
60        return p; //返回右子樹裏最左的非空節點
61    } else {
62        Entry<K,V> p = t.parent;
63        Entry<K,V> ch = t;
64        while (p != null && ch == p.right) {
65            ch = p;
66            p = p.parent;
67        }
68        return p;
69    }
70}
複製代碼


     經過上述邏輯咱們知道,刪除節點分爲下面幾種狀況:微信

  • 有左右孩子節點
  • 只有一個孩子節點
  • 無孩子節點(葉子節點)【狀況最爲複雜,後面會詳細講解】
2.二、代碼整理成流程圖:
圖注:刪除元素流程圖
圖注:刪除元素流程圖

2.三、示例:
  1. 有左右孩子,且無替代節點:【先調整後刪除的流程】
    圖注:有左右孩子但無替代節點
    圖注:有左右孩子但無替代節點

     無替代節點,說明後繼節點走到了葉子節點。須要調整說明後繼節點爲黑色,正如上圖所示。此時就轉化爲對於葉子節點10的刪除操做了,處理過程請日後看。app

  1. 有左右孩子,且有替代節點:【先刪除後調整的流程】
    圖注:有左右孩子且有替代節點
    圖注:有左右孩子且有替代節點

     有替代節點,說明後繼節點未走到葉子節點,那麼後繼節點確定只有一個孩子節點,不然必能夠走到葉子節點(此處能夠畫圖體會體會)。一個節點只有一個孩子節點,那麼只有一種可能,就是節點爲黑,孩子爲紅,不然樹不平衡,正如上圖所示。此時,替代節點爲紅色,調整邏輯就很簡單了,就是將該紅色節點改爲黑色節點便可。優化

  1. 只有一個孩子節點,必有替代節點:【先刪除後調整的流程】
    圖注:只有一個孩子->必有替代節點
    圖注:只有一個孩子->必有替代節點

     只有一個孩子節點,替代節點獲取的規則就是要麼左節點,要麼右節點,因此必有替代節點。一個節點只有一個孩子節點,那麼只有一種可能,就是節點爲黑,孩子爲紅,不然樹不平衡,正如上圖所示。此時,替代節點爲紅色,調整邏輯就很簡單了,就是將該紅色節點改爲黑色節點便可。spa

2.四、總結:

     經過上述的分析,凡是先進行刪除,後進行調整的狀況,其調整邏輯都很簡單,就是隻須要將節點顏色從紅色變成黑色便可。先進行調整,後進行刪除的狀況,纔是邏輯最爲複雜的,請繼續往下看。3d

三、對樹進行刪除後的調整:

 1private void fixAfterDeletion(Entry<K,V> x) {
2    //只要x不爲根節點且爲黑色,就須要調整
3    while (x != root && colorOf(x) == BLACK) {
4        //X是否爲父親節點的左節點
5        if (x == leftOf(parentOf(x))) {
6            //獲取X右兄弟節點sib
7            Entry<K,V> sib = rightOf(parentOf(x));
8            //X兄弟節點爲紅色轉化爲X兄弟節點爲黑色的狀況
9            if (colorOf(sib) == RED) {
10                //設置X兄弟節點爲黑色
11                setColor(sib, BLACK);
12                //設置X父親節點爲紅色
13                setColor(parentOf(x), RED);
14                //以X父親節點爲中心進行左旋
15                rotateLeft(parentOf(x));
16                //sib指向X的兄弟節點
17                sib = rightOf(parentOf(x));
18            }
19
20            //兄弟節點左右節點都爲黑色
21            if (colorOf(leftOf(sib))  == BLACK &&
22                colorOf(rightOf(sib)) == BLACK) {
23                //兄弟節點改爲紅色
24                setColor(sib, RED);
25                //X指向父節點
26                x = parentOf(x);
27            } else {
28                //X遠侄節點爲黑色轉化爲X遠侄節點爲紅色的狀況
29                if (colorOf(rightOf(sib)) == BLACK) {
30                    //近侄節點改爲黑色
31                    setColor(leftOf(sib), BLACK);
32                    //兄弟節點改爲紅色
33                    setColor(sib, RED);
34                    //以兄弟節點爲中心進行右旋
35                    rotateRight(sib);
36                    //sib指向X右兄弟節點
37                    sib = rightOf(parentOf(x));
38                }
39                //設置sib爲X父節點顏色
40                setColor(sib, colorOf(parentOf(x)));
41                //設置X父節點爲黑色
42                setColor(parentOf(x), BLACK);
43                //設置遠侄節點爲黑色
44                setColor(rightOf(sib), BLACK);
45                //以X父親節點爲中心進行左旋
46                rotateLeft(parentOf(x));
47                //X指向根,結束循環
48                x = root;
49            }
50        } else { // symmetric
51            //獲取X左兄弟節點sib
52            Entry<K,V> sib = leftOf(parentOf(x));
53            //X兄弟節點爲紅色轉化爲X兄弟節點爲黑色的狀況
54            if (colorOf(sib) == RED) {
55                //設置X兄弟節點爲黑色
56                setColor(sib, BLACK);
57                //設置X父親節點爲紅色
58                setColor(parentOf(x), RED);
59                //以X父親節點爲中心進行右旋
60                rotateRight(parentOf(x));
61                //sib指向X的兄弟節點
62                sib = leftOf(parentOf(x));
63            }
64
65            //兄弟節點左右節點都爲黑色
66            if (colorOf(rightOf(sib)) == BLACK &&
67                colorOf(leftOf(sib)) == BLACK) {
68                //兄弟節點改爲紅色
69                setColor(sib, RED);
70                //X指向父節點
71                x = parentOf(x);
72            } else {
73                //X遠侄節點爲黑色轉化爲X遠侄節點爲紅色的狀況
74                if (colorOf(leftOf(sib)) == BLACK) {
75                    //近侄節點改爲黑色
76                    setColor(rightOf(sib), BLACK);
77                    //兄弟節點改爲紅色
78                    setColor(sib, RED);
79                    //以兄弟節點爲中心進行左旋
80                    rotateLeft(sib);
81                    //sib指向X左兄弟節點
82                    sib = leftOf(parentOf(x));
83                }
84                //設置sib爲X父節點顏色
85                setColor(sib, colorOf(parentOf(x)));
86                //設置X父節點爲黑色
87                setColor(parentOf(x), BLACK);
88                //設置遠侄節點爲黑色
89                setColor(leftOf(sib), BLACK);
90                //以X父親節點爲中心進行右旋
91                rotateRight(parentOf(x));
92                //X指向根,結束循環
93                x = root;
94            }
95        }
96    }
97
98    //設置X爲黑色
99    setColor(x, BLACK);
100}
複製代碼
3.一、代碼整理爲流程圖:
圖注:刪除操做的平衡流程圖
圖注:刪除操做的平衡流程圖

高清圖請看:https://user-gold-cdn.xitu.io/2018/9/7/165b3848ec1c7eee?w=1914&h=2314&f=jpeg&s=382012

3.二、優化的流程圖:
圖注:刪除操做的平衡流程圖-優化版
圖注:刪除操做的平衡流程圖-優化版

高清圖請看:https://user-gold-cdn.xitu.io/2018/9/7/165b3848ec062ee1?w=1246&h=1341&f=jpeg&s=191723

3.三、流程圖解析說明:
  1. 兄弟節點爲黑色,遠侄節點爲紅色(刪除節點X爲葉子節點,且爲黑色[若是爲紅直接刪除便可])
    code

    圖注:兄弟爲黑,遠侄爲紅
    圖注:兄弟爲黑,遠侄爲紅

  2. 兄弟節點爲黑色,遠侄節點爲黑色(刪除節點X爲葉子節點,且爲黑色[若是爲紅直接刪除便可])
    orm

    圖注:兄弟爲黑,遠侄爲黑
    圖注:兄弟爲黑,遠侄爲黑

         兄弟節點爲黑色,而且刪除節點X爲黑色,且爲葉子節點->遠侄節點若是爲黑色,必然是null節點,不然樹不平衡。

  3. 兄弟節點爲黑色,遠侄近侄節點都爲黑色(刪除節點X爲葉子節點,且爲黑色[若是爲紅直接刪除便可])
    cdn

    圖注:兄弟爲黑,遠近侄爲黑
    圖注:兄弟爲黑,遠近侄爲黑

  4. 兄弟節點爲紅色(刪除節點X爲葉子節點,且爲黑色[若是爲紅直接刪除便可])

    圖注:兄弟爲紅
    圖注:兄弟爲紅

         兄弟節點爲紅色,X爲黑色,且是葉子->遠侄節點和近侄節點必爲null節點,不然樹不平衡。

3.四、示例:
  1. 兄弟節點爲紅色

    圖注:兄弟爲紅
    圖注:兄弟爲紅

    高清圖請看:https://user-gold-cdn.xitu.io/2018/9/7/165b33e01bf074ab?w=7315&h=640&f=jpeg&s=558111

  2. 兄弟節點爲黑色,遠侄節點爲紅色

    圖注:兄弟爲黑,遠侄爲紅
    圖注:兄弟爲黑,遠侄爲紅

    高清圖請看:https://user-gold-cdn.xitu.io/2018/9/7/165b33e02c61c338?w=4471&h=640&f=jpeg&s=347442

  3. 兄弟節點爲黑色,遠侄節點爲黑色

    圖注:兄弟爲黑,遠侄爲黑
    圖注:兄弟爲黑,遠侄爲黑

    高清圖請看:https://user-gold-cdn.xitu.io/2018/9/7/165b3848ed9e566b?w=6613&h=640&f=jpeg&s=480598

  4. 兄弟節點爲黑色,遠侄近侄節點都爲黑色

    圖注:兄弟爲黑,遠近侄爲黑
    圖注:兄弟爲黑,遠近侄爲黑

    高清圖請看:https://user-gold-cdn.xitu.io/2018/9/7/165b33e03146c697?w=3902&h=640&f=jpeg&s=273947

總結

     紅黑樹的刪除相對於插入而言,複雜了很多。可是隻要時刻記住五條性質,對於包含的每種場景動手去練習,我想理解它應該也不是難事。
     文章的最後,感謝你們的支持,歡迎掃描下方二維碼,進行關注。若有任何疑問,歡迎你們留言。

相關文章
相關標籤/搜索