微信公衆號:I am CR7
若有問題或建議,請在下方留言;
最近更新:2018-09-05java
經過上一篇文章,介紹了二分查找樹的缺陷,引出了紅黑樹的介紹。進一步分析TreeMap中插入元素的源碼,最後藉助示例來加深對於紅黑樹的理解。詳細請看TreeMap之元素插入node
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}
複製代碼
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}
複製代碼
經過上述邏輯咱們知道,刪除節點分爲下面幾種狀況:微信
無替代節點,說明後繼節點走到了葉子節點。須要調整說明後繼節點爲黑色,正如上圖所示。此時就轉化爲對於葉子節點10的刪除操做了,處理過程請日後看。app
有替代節點,說明後繼節點未走到葉子節點,那麼後繼節點確定只有一個孩子節點,不然必能夠走到葉子節點(此處能夠畫圖體會體會)。一個節點只有一個孩子節點,那麼只有一種可能,就是節點爲黑,孩子爲紅,不然樹不平衡,正如上圖所示。此時,替代節點爲紅色,調整邏輯就很簡單了,就是將該紅色節點改爲黑色節點便可。優化
只有一個孩子節點,替代節點獲取的規則就是要麼左節點,要麼右節點,因此必有替代節點。一個節點只有一個孩子節點,那麼只有一種可能,就是節點爲黑,孩子爲紅,不然樹不平衡,正如上圖所示。此時,替代節點爲紅色,調整邏輯就很簡單了,就是將該紅色節點改爲黑色節點便可。spa
經過上述的分析,凡是先進行刪除,後進行調整的狀況,其調整邏輯都很簡單,就是隻須要將節點顏色從紅色變成黑色便可。先進行調整,後進行刪除的狀況,纔是邏輯最爲複雜的,請繼續往下看。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}
複製代碼
兄弟節點爲黑色,遠侄節點爲紅色(刪除節點X爲葉子節點,且爲黑色[若是爲紅直接刪除便可])
code
兄弟節點爲黑色,遠侄節點爲黑色(刪除節點X爲葉子節點,且爲黑色[若是爲紅直接刪除便可])
orm
兄弟節點爲黑色,遠侄近侄節點都爲黑色(刪除節點X爲葉子節點,且爲黑色[若是爲紅直接刪除便可])
cdn
兄弟節點爲紅色(刪除節點X爲葉子節點,且爲黑色[若是爲紅直接刪除便可])
兄弟節點爲紅色
兄弟節點爲黑色,遠侄節點爲紅色
兄弟節點爲黑色,遠侄節點爲黑色
兄弟節點爲黑色,遠侄近侄節點都爲黑色
紅黑樹的刪除相對於插入而言,複雜了很多。可是隻要時刻記住五條性質,對於包含的每種場景動手去練習,我想理解它應該也不是難事。
文章的最後,感謝你們的支持,歡迎掃描下方二維碼,進行關注。若有任何疑問,歡迎你們留言。