概述
文章的內(nèi)容基于JDK1.7進(jìn)行分析膳汪,之所以選用這個(gè)版本茄袖,是因?yàn)?.8的有些類做了改動(dòng)渺贤,增加了閱讀的難度雏胃,雖然是1.7,但是對于1.8做了重大改動(dòng)的內(nèi)容志鞍,文章也會(huì)進(jìn)行說明瞭亮。
TreeMap實(shí)現(xiàn)了SotredMap接口,它是有序的集合固棚。而且是一個(gè)紅黑樹結(jié)構(gòu)统翩,每個(gè)key-value都作為一個(gè)紅黑樹的節(jié)點(diǎn)。如果在調(diào)用TreeMap的構(gòu)造函數(shù)時(shí)沒有指定比較器此洲,則根據(jù)key執(zhí)行自然排序厂汗。這點(diǎn)會(huì)在接下來的代碼中做說明,如果指定了比較器則按照比較器來進(jìn)行排序呜师。
數(shù)據(jù)結(jié)構(gòu)
繼承關(guān)系
public class TreeMap<K,V>extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, Serializable
實(shí)現(xiàn)接口
Serializable, Cloneable, Map<K,V>, NavigableMap<K,V>, SortedMap<K,V>
基本屬性
private final Comparator<? super K> comparator; //比較器娶桦,是自然排序,還是定制排序 汁汗,使用final修飾衷畦,表明一旦賦值便不允許改變
private transient Entry<K,V> root = null; //紅黑樹的根節(jié)點(diǎn)
private transient int size = 0; //TreeMap中存放的鍵值對的數(shù)量
private transient int modCount = 0; //修改的次數(shù)
源碼解析
由于TreeMap中源碼較長,接下來將分段解析部分源碼知牌。既然是紅黑樹存儲(chǔ)祈争,肯定要有數(shù)據(jù)結(jié)構(gòu)(Node)節(jié)點(diǎn)的〗谴纾看一下TreeMap中關(guān)于節(jié)點(diǎn)的定義部分菩混。
數(shù)據(jù)結(jié)構(gòu)
static final class Entry<K,V> implements Map.Entry<K,V> {
K key; //鍵
V value; //值
Entry<K,V> left = null; //左孩子節(jié)點(diǎn)
Entry<K,V> right = null; //右孩子節(jié)點(diǎn)
Entry<K,V> parent; //父節(jié)點(diǎn)
boolean color = BLACK; //節(jié)點(diǎn)的顏色,在紅黑樹種扁藕,只有兩種顏色墨吓,紅色和黑色
//構(gòu)造方法,用指定的key,value ,parent初始化纹磺,color默認(rèn)為黑色
Entry(K key, V value, Entry<K,V> parent) {
this.key = key;
this.value = value;
this.parent = parent;
}
//返回key
public K getKey() {
return key;
}
//返回該節(jié)點(diǎn)對應(yīng)的value
public V getValue() {
return value;
}
//替換節(jié)點(diǎn)的值帖烘,并返回舊值
public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue;
}
//重寫equals()方法
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
//兩個(gè)節(jié)點(diǎn)的key相等,value相等橄杨,這兩個(gè)節(jié)點(diǎn)才相等
return valEquals(key,e.getKey()) && valEquals(value,e.getValue());
}
//重寫hashCode()方法
public int hashCode() {
int keyHash = (key==null ? 0 : key.hashCode());
int valueHash = (value==null ? 0 : value.hashCode());
//key和vale hash值得異或運(yùn)算秘症,相同則為零照卦,不同則為1
return keyHash ^ valueHash;
}
//重寫toString()方法
public String toString() {
return key + "=" + value;
}
}
構(gòu)造方法
//構(gòu)造方法,comparator用鍵的順序做比較
public TreeMap() {
comparator = null;
}
//構(gòu)造方法乡摹,提供比較器役耕,用指定比較器排序
public TreeMap(Comparator<? super K> comparator) {
his.comparator = comparator;
}
//將m中的元素轉(zhuǎn)化daoTreeMap中,按照鍵的順序做比較排序
public TreeMap(Map<? extends K, ? extends V> m) {
comparator = null;
putAll(m);
}
//構(gòu)造方法聪廉,指定的參數(shù)為SortedMap
//采用m的比較器排序
public TreeMap(SortedMap<K, ? extends V> m) {
comparator = m.comparator();
try {
buildFromSorted(m.size(), m.entrySet().iterator(), null, null);
} catch (java.io.IOException cannotHappen) {
} catch (ClassNotFoundException cannotHappen) {
}
}
TreeMap提供了四個(gè)構(gòu)造方法瞬痘,實(shí)現(xiàn)了方法的重載。無參構(gòu)造方法中比較器的值為null,采用自然排序的方法板熊,如果指定了比較器則稱之為定制排序.
- 自然排序:TreeMap的所有key必須實(shí)現(xiàn)Comparable接口框全,所有的key都是同一個(gè)類的對象
- 定制排序:創(chuàng)建TreeMap對象傳入了一個(gè)Comparator對象,該對象負(fù)責(zé)對TreeMap中所有的key進(jìn)行排序干签,采用定制排序不要求Map的key實(shí)現(xiàn)Comparable接口津辩。等下面分析到比較方法的時(shí)候在分析這兩種比較有何不同。
對于Map來說容劳,使用的最多的就是put()/get()/remove()等方法喘沿,下面依次進(jìn)行分析
put()
public V put(K key, V value) {
Entry<K,V> t = root; //紅黑樹的根節(jié)點(diǎn)
if (t == null) { //紅黑樹是否為空
compare(key, key); // type (and possibly null) check
//構(gòu)造根節(jié)點(diǎn),因?yàn)楦?jié)點(diǎn)沒有父節(jié)點(diǎn)竭贩,傳入null值蚜印。
root = new Entry<>(key, value, null);
size = 1; //size值加1
modCount++; //改變修改的次數(shù)
return null; //返回null
}
int cmp;
Entry<K,V> parent; //定義節(jié)點(diǎn)
// split comparator and comparable paths
Comparator<? super K> cpr = comparator; //獲取比較器
if (cpr != null) { //如果定義了比較器土辩,采用自定義比較器進(jìn)行比較
do {
parent = t; //將紅黑樹根節(jié)點(diǎn)賦值給parent
cmp = cpr.compare(key, t.key); //比較key, 與根節(jié)點(diǎn)的大小
if (cmp < 0) //如果key < t.key , 指向左子樹
t = t.left; //t = t.left , t == 它的做孩子節(jié)點(diǎn)
else if (cmp > 0)
t = t.right; //如果key > t.key , 指向它的右孩子節(jié)點(diǎn)
else
return t.setValue(value); //如果它們相等擂涛,替換key的值
} while (t != null); //循環(huán)遍歷
}
else {
//自然排序方式,沒有指定比較器
if (key == null)
throw new NullPointerException(); //拋出異常
Comparable<? super K> k = (Comparable<? super K>) key; //類型轉(zhuǎn)換
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0) // key < t.key
t = t.left; //左孩子
else if (cmp > 0) // key > t.key
t = t.right; //右孩子
else
return t.setValue(value); //t == t.key , 替換value值
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent); //創(chuàng)建新節(jié)點(diǎn)襟企,并制定父節(jié)點(diǎn)
//根據(jù)比較結(jié)果肪获,決定新節(jié)點(diǎn)為父節(jié)點(diǎn)的左孩子或者右孩子
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e); //新插入節(jié)點(diǎn)后重新調(diào)整紅黑樹
size++;
modCount++;
return null;
}
//比較方法,如果comparator==null ,采用comparable.compartTo進(jìn)行比較柒傻,否則采用指定比較器比較大小
final int compare(Object k1, Object k2) {
return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
: comparator.compare((K)k1, (K)k2);
}
private void fixAfterInsertion(Entry<K,V> x) {
//插入的節(jié)點(diǎn)默認(rèn)的顏色為紅色
x.color = RED; //
//情形1: 新節(jié)點(diǎn)x 是樹的根節(jié)點(diǎn)孝赫,沒有父節(jié)點(diǎn)不需要任何操作
//情形2: 新節(jié)點(diǎn)x 的父節(jié)點(diǎn)顏色是黑色的,也不需要任何操作
while (x != null && x != root && x.parent.color == RED) {
//情形3:新節(jié)點(diǎn)x的父節(jié)點(diǎn)顏色是紅色的
//判斷x的節(jié)點(diǎn)的父節(jié)點(diǎn)位置红符,是否屬于左孩子
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
//獲取x節(jié)點(diǎn)的父節(jié)點(diǎn)的兄弟節(jié)點(diǎn)青柄,上面語句已經(jīng)判斷出x節(jié)點(diǎn)的父節(jié)點(diǎn)為左孩子,所以直接取右孩子
Entry<K,V> y = rightOf(parentOf(parentOf(x)));
//判斷是否x節(jié)點(diǎn)的父節(jié)點(diǎn)的兄弟節(jié)點(diǎn)為紅色预侯。
if (colorOf(y) == RED) {
setColor(parentOf(x), BLACK); // x節(jié)點(diǎn)的父節(jié)點(diǎn)設(shè)置為黑色
setColor(y, BLACK); // y節(jié)點(diǎn)的顏色設(shè)置為黑色
setColor(parentOf(parentOf(x)), RED); // x.parent.parent設(shè)置為紅色
x = parentOf(parentOf(x)); // x == x.parent.parent ,進(jìn)行遍歷致开。
} else {
//x的父節(jié)點(diǎn)的兄弟節(jié)點(diǎn)是黑色或者缺少的
if (x == rightOf(parentOf(x))) { //判斷x節(jié)點(diǎn)是否為父節(jié)點(diǎn)的右孩子
x = parentOf(x); //x == 父節(jié)點(diǎn)
rotateLeft(x); //左旋轉(zhuǎn)操作
}
//x節(jié)點(diǎn)是其父的左孩子
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED); //上面兩句將x.parent 和x.parent.parent的顏色做調(diào)換
rotateRight(parentOf(parentOf(x))); //進(jìn)行右旋轉(zhuǎn)
}
} else {
Entry<K,V> y = leftOf(parentOf(parentOf(x))); //y 是x 節(jié)點(diǎn)的祖父節(jié)點(diǎn)的左孩子
if (colorOf(y) == RED) { //判斷顏色
setColor(parentOf(x), BLACK); //父節(jié)點(diǎn)設(shè)置為黑色
setColor(y, BLACK); //父節(jié)點(diǎn)的兄弟節(jié)點(diǎn)設(shè)置為黑色
setColor(parentOf(parentOf(x)), RED); //祖父節(jié)點(diǎn)設(shè)置為紅色
x = parentOf(parentOf(x)); //將祖父節(jié)點(diǎn)作為新插入的節(jié)點(diǎn),遍歷調(diào)整
} else {
if (x == leftOf(parentOf(x))) { //x 是其父親的左孩子
x = parentOf(x);
rotateRight(x); //以父節(jié)點(diǎn)為旋轉(zhuǎn)點(diǎn)萎馅,進(jìn)行右旋操作
}
setColor(parentOf(x), BLACK); //父節(jié)點(diǎn)為設(shè)置為黑色
setColor(parentOf(parentOf(x)), RED); //祖父節(jié)點(diǎn)設(shè)置為紅色
rotateLeft(parentOf(parentOf(x))); //以父節(jié)點(diǎn)為旋轉(zhuǎn)點(diǎn)双戳,進(jìn)行左旋操作
}
}
}
root.color = BLACK; //通過節(jié)點(diǎn)位置的調(diào)整,最終將紅色的節(jié)點(diǎn)條調(diào)換到了根節(jié)點(diǎn)的位置糜芳,根節(jié)點(diǎn)重新設(shè)置為黑色
}
紅黑樹是一個(gè)更高效的檢索二叉樹飒货,有如下特點(diǎn):
- 每個(gè)節(jié)點(diǎn)只能是紅色或者黑色
- 根節(jié)點(diǎn)永遠(yuǎn)是黑色的
- 所有的葉子的子節(jié)點(diǎn)都是空節(jié)點(diǎn)魄衅,并且都是黑色的
- 每個(gè)紅色節(jié)點(diǎn)的兩個(gè)子節(jié)點(diǎn)都是黑色的(不會(huì)有兩個(gè)連續(xù)的紅色節(jié)點(diǎn))
- 從任一個(gè)節(jié)點(diǎn)到其子樹中每個(gè)葉子節(jié)點(diǎn)的路徑都包含相同數(shù)量的黑色節(jié)點(diǎn)(葉子節(jié)點(diǎn)到根節(jié)點(diǎn)的黑色節(jié)點(diǎn)數(shù)量每條路徑都相同)
上面的代碼,詳細(xì)的標(biāo)注了每條語句的作用塘辅,但是我相信晃虫,如果你沒有一定的功力,即使注釋已經(jīng)很詳細(xì)了扣墩,你也會(huì)是一臉懵逼 哲银,二臉懵逼,全腦懵逼中呻惕,下面配合圖片來梳理一下代碼所表示的含義:
當(dāng)一個(gè)默認(rèn)為紅色的節(jié)點(diǎn)插入樹中荆责,其實(shí)對應(yīng)的是7中可能發(fā)生的情況,分別進(jìn)行敘述:
情形1:新插入的節(jié)點(diǎn)時(shí)紅黑樹的根節(jié)點(diǎn)蟆融,沒有父節(jié)點(diǎn)草巡,無需任何的操作,直接將顏色設(shè)置為黑色就可以了
情形2:新節(jié)點(diǎn)的父節(jié)點(diǎn)顏色是黑色的型酥,新插入的節(jié)點(diǎn)是紅色的山憨。也無需任何的操作。因?yàn)樾鹿?jié)點(diǎn)的插入并沒有影響到紅黑書的特點(diǎn)
-
情形3:新節(jié)點(diǎn)的父節(jié)點(diǎn)(左孩子節(jié)點(diǎn))顏色是紅色的弥喉,而父節(jié)點(diǎn)的兄弟節(jié)點(diǎn)顏色也是紅色的郁竟。那么情況就出現(xiàn)了,此時(shí)插入的節(jié)點(diǎn)就違反了紅黑樹的特點(diǎn)4 由境,需要對紅黑樹進(jìn)行調(diào)整棚亩。 操作看下圖:
父節(jié)點(diǎn)和父親的兄弟節(jié)點(diǎn)都為紅色.png
調(diào)整操作如上圖,將父節(jié)點(diǎn)和父節(jié)點(diǎn)的兄弟節(jié)點(diǎn)虏杰,都修改為紅色讥蟆,然后將祖父節(jié)點(diǎn)修改為紅色,因?yàn)樾薷牧俗娓腹?jié)點(diǎn)的顏色纺阔,祖父節(jié)點(diǎn)可能會(huì)發(fā)生顏色的沖突瘸彤,所以將新插入的節(jié)點(diǎn)修改為祖父節(jié)點(diǎn),在進(jìn)行調(diào)整笛钝。
-
情形4:父節(jié)點(diǎn)(左孩子節(jié)點(diǎn))的顏色為紅色质况,父節(jié)點(diǎn)的兄弟節(jié)點(diǎn)的顏色為黑色或者為null,新插入的節(jié)點(diǎn)為父節(jié)點(diǎn)的右孩子節(jié)點(diǎn)玻靡。如下圖:
父節(jié)點(diǎn)為紅色结榄,父節(jié)點(diǎn)的兄弟節(jié)點(diǎn)為黑色或者null.png
此時(shí)以父節(jié)點(diǎn)為旋轉(zhuǎn)點(diǎn),就新插入的節(jié)點(diǎn)進(jìn)行左旋操作囤捻。便變成了情形5對應(yīng)的情況臼朗,將執(zhí)行情形5的操作
-
情形5:父節(jié)點(diǎn)(左孩子節(jié)點(diǎn))的顏色為紅色,父節(jié)點(diǎn)的兄弟節(jié)點(diǎn)顏色為黑色或者null,新插入節(jié)點(diǎn)為父親的左孩子節(jié)點(diǎn)。如下圖:
父節(jié)點(diǎn)為紅色依溯,新節(jié)點(diǎn)為左孩子節(jié)點(diǎn).png 情形6 和情形7的操作與情形4和情形5的操作相同老厌,它們之前的區(qū)別是父節(jié)點(diǎn)為有孩子節(jié)點(diǎn),再次不再贅述黎炉。
總結(jié)
關(guān)于紅黑樹的節(jié)點(diǎn)插入操作枝秤,首先是改變新節(jié)點(diǎn),新節(jié)點(diǎn)的父節(jié)點(diǎn)慷嗜,祖父節(jié)點(diǎn)淀弹,和新節(jié)點(diǎn)的顏色,能在當(dāng)前分支通過節(jié)點(diǎn)的旋轉(zhuǎn)改變的庆械,則通過此種操作薇溃,來滿足紅黑書的特點(diǎn)。如果當(dāng)前相關(guān)節(jié)點(diǎn)的旋轉(zhuǎn)解決不了紅黑樹的沖突缭乘,則通過將紅色的節(jié)點(diǎn)移動(dòng)到根節(jié)點(diǎn)解決沐序,最后在將根節(jié)點(diǎn)設(shè)置為黑色
remove()
public V remove(Object key) {
Entry<K,V> p = getEntry(key); //根據(jù)key查找節(jié)點(diǎn),并返回該節(jié)點(diǎn)
if (p == null)
return null;
V oldValue = p.value; //獲取key對應(yīng)的值
deleteEntry(p); //刪除節(jié)點(diǎn)
return oldValue; //返回key對應(yīng)的值
}
final Entry<K,V> getEntry(Object key) {
//根據(jù)鍵尋找節(jié)點(diǎn)堕绩,有非為兩種方式策幼,如果定制了比較器,采用定制排序方式奴紧,否則使用自然排序
if (comparator != null)
return getEntryUsingComparator(key); //循環(huán)遍歷樹特姐,尋找和key相等的節(jié)點(diǎn)
if (key == null)
throw new NullPointerException();
Comparable<? super K> k = (Comparable<? super K>) key;
Entry<K,V> p = root;
while (p != null) { //循環(huán)遍歷樹,尋找和key相等的節(jié)點(diǎn)
int cmp = k.compareTo(p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
return null;
}
//刪除節(jié)點(diǎn)
private void deleteEntry(Entry<K,V> p) {
modCount++; //記錄修改的次數(shù)
size--; //數(shù)量減1
//當(dāng)前節(jié)點(diǎn)的兩個(gè)孩子都不為空
if (p.left != null && p.right != null) {
//尋找繼承者黍氮,繼承者為當(dāng)前節(jié)點(diǎn)的右孩子節(jié)點(diǎn)或者右孩子節(jié)點(diǎn)的最小左孩子
Entry<K,V> s = successor(p);
p.key = s.key; //key - value 的替換 唐含,并沒有替換顏色
p.value = s.value;
p = s; //指向繼承者
} // p has 2 children
// Start fixup at replacement node, if it exists.
//開始修復(fù)樹結(jié)構(gòu),繼承者的左孩子不為空沫浆,返回左孩子捷枯,否則返回右孩子
//不可能存在左右兩個(gè)孩子都存在的情況,successor尋找的就是最小節(jié)點(diǎn)专执,它的左孩子節(jié)點(diǎn)為null
Entry<K,V> replacement = (p.left != null ? p.left : p.right);
if (replacement != null) {
// Link replacement to parent
//已經(jīng)被選為繼承者淮捆,當(dāng)前擁有的一切放棄,所以將孩子交給爺爺撫養(yǎng)
replacement.parent = p.parent;
//p節(jié)點(diǎn)沒有父節(jié)點(diǎn)他炊,則指向根節(jié)點(diǎn)
if (p.parent == null)
root = replacement;
//如果p為左孩子,如果p為左孩子已艰,則將p.parent.left = p.left
else if (p == p.parent.left)
p.parent.left = replacement;
else
p.parent.right = replacement;
//刪除p節(jié)點(diǎn)到左右分支痊末,和父節(jié)點(diǎn)的引用
p.left = p.right = p.parent = null;
// Fix replacement
if (p.color == BLACK)
//恢復(fù)顏色分配
fixAfterDeletion(replacement);
} else if (p.parent == null) { // return if we are the only node.
//紅黑書中父節(jié)點(diǎn)為空的只能是根節(jié)點(diǎn)。
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;
}
}
}
private void fixAfterDeletion(Entry<K,V> x) {
//不是根節(jié)點(diǎn)哩掺,顏色為黑色凿叠,調(diào)整結(jié)構(gòu)
while (x != root && colorOf(x) == BLACK) {
//判斷x是否為左孩子
if (x == leftOf(parentOf(x))) {
//x的兄弟節(jié)點(diǎn)
Entry<K,V> sib = rightOf(parentOf(x));
//若兄弟節(jié)點(diǎn)是紅色
if (colorOf(sib) == RED) {
setColor(sib, BLACK); //設(shè)置兄弟節(jié)點(diǎn)變?yōu)楹谏? setColor(parentOf(x), RED); //父節(jié)點(diǎn)設(shè)置為紅色
rotateLeft(parentOf(x)); //左旋父節(jié)點(diǎn)
sib = rightOf(parentOf(x)); //重新設(shè)置x的兄弟節(jié)點(diǎn)
}
if (colorOf(leftOf(sib)) == BLACK &&
colorOf(rightOf(sib)) == BLACK) {
setColor(sib, RED); //兄弟節(jié)點(diǎn)的兩個(gè)孩子都是黑色的重新設(shè)置兄弟節(jié)點(diǎn)的顏色,修改為紅色
x = parentOf(x); //將x定位到父節(jié)點(diǎn)
} else {
if (colorOf(rightOf(sib)) == BLACK) { //兄弟節(jié)點(diǎn)的右孩子是黑色的,左孩子是紅色的
setColor(leftOf(sib), BLACK); //設(shè)置左孩子節(jié)點(diǎn)為黑色
setColor(sib, RED); //兄弟節(jié)點(diǎn)為紅色
rotateRight(sib); //右旋
sib = rightOf(parentOf(x)); //右旋后重新設(shè)置兄弟節(jié)點(diǎn)
}
setColor(sib, colorOf(parentOf(x))); //兄弟節(jié)點(diǎn)顏色設(shè)置和父節(jié)點(diǎn)的顏色相同
setColor(parentOf(x), BLACK); //父節(jié)點(diǎn)設(shè)置為黑色
setColor(rightOf(sib), BLACK); //將兄弟節(jié)點(diǎn)的有孩子設(shè)置為黑色
rotateLeft(parentOf(x)); //左旋
x = root; //設(shè)置x為根節(jié)點(diǎn)
}
} else { // symmetric
//x為父節(jié)點(diǎn)的右節(jié)點(diǎn)盒件,參考上面的操作
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);
}
刪除紅黑樹的操作比插入操作要稍微麻煩一點(diǎn)蹬碧,分為兩步:
- 以排序二叉樹的方法刪除指定節(jié)點(diǎn)。刪除的節(jié)點(diǎn)存在三種情況:
- 被刪除節(jié)點(diǎn)炒刁,沒有左右孩子節(jié)點(diǎn)恩沽,直接刪除即可
- 被刪除節(jié)點(diǎn),有一個(gè)孩子節(jié)點(diǎn)翔始,那么讓它的孩子節(jié)點(diǎn)指向它的父節(jié)點(diǎn)即可
- 本刪除的節(jié)點(diǎn)罗心,有兩個(gè)非空的孩子節(jié)點(diǎn),那么需要找到該節(jié)點(diǎn)的前驅(qū)或者后繼節(jié)點(diǎn)城瞎,更換元素值渤闷,在將前驅(qū)或者后繼節(jié)點(diǎn)刪除(任意一個(gè)節(jié)點(diǎn)的前驅(qū)或者后繼都必定至多有一個(gè)非空的子節(jié)點(diǎn),可以按照前面的兩種情形進(jìn)行操作)
- 進(jìn)行顏色的調(diào)換和樹的旋轉(zhuǎn)脖镀,滿足紅黑樹的特征
下面來分情形討論一下可能發(fā)生的情況:
- 情形1:被刪除的節(jié)點(diǎn)為根節(jié)點(diǎn)或者顏色為空色飒箭,此時(shí)刪除該節(jié)點(diǎn)不影響紅黑樹的特點(diǎn)。無需操作
-
情形2:被刪除節(jié)點(diǎn)為黑色蜒灰,兄弟節(jié)點(diǎn)為紅色弦蹂,如下圖:
兄弟節(jié)點(diǎn)為紅色.png
若刪除上圖中的x節(jié)點(diǎn),將缺少一個(gè)黑節(jié)點(diǎn)卷员,與紅黑樹的性質(zhì)沖突盈匾,所以修改sib顏色為黑色,設(shè)置p節(jié)點(diǎn)為紅色毕骡,并進(jìn)行左旋操作削饵。在進(jìn)行后續(xù)的處理。
-
情形3:被刪除節(jié)點(diǎn)為黑色未巫,x節(jié)點(diǎn)的兄弟節(jié)點(diǎn)的子節(jié)點(diǎn)都是黑色窿撬,如下圖:
x節(jié)點(diǎn)的兄弟節(jié)點(diǎn)的子節(jié)點(diǎn)都是黑色.png
x節(jié)點(diǎn)是黑色的,兄弟節(jié)點(diǎn)(黑色的)的子節(jié)點(diǎn)也是黑色的叙凡,p節(jié)點(diǎn)的顏色無法確定劈伴,有可能是紅色的,也有可能是黑色的握爷。如果是紅色的直接設(shè)置為黑色即可跛璧,如果為黑色的,則需要將x定位的p節(jié)點(diǎn)新啼,在進(jìn)行處理追城。
-
情形4:被刪除節(jié)點(diǎn)為黑色,x的兄弟節(jié)點(diǎn)的右自子節(jié)點(diǎn)為黑色燥撞。如下圖:
x兄弟節(jié)點(diǎn)的右子節(jié)點(diǎn)為黑色.png
情形4的調(diào)整為了轉(zhuǎn)變成情形5的情況座柱,來進(jìn)行處理
-
情形5:被刪除節(jié)點(diǎn)為黑色迷帜,x的兄弟節(jié)點(diǎn)右子節(jié)點(diǎn)為紅色。如下圖:
x兄弟節(jié)點(diǎn)的右子節(jié)點(diǎn)為紅色色洞,左子節(jié)點(diǎn)的顏色未知.png
sib的左子節(jié)點(diǎn)的顏色不確定戏锹,可能是紅色也可能是黑色,但是對它并沒有什么影響火诸,因?yàn)樽儞Q前后它的上層分支的黑色節(jié)點(diǎn)數(shù)并沒有改變锦针。
上面的情形只是針對刪除的節(jié)點(diǎn)是左孩子的情況,進(jìn)行的分析惭蹂,被刪除的節(jié)點(diǎn)也可能是右分支伞插。情況完全相同只不過左右順序發(fā)生了顛倒,不再進(jìn)行復(fù)述盾碗。
至此TreeMap中實(shí)現(xiàn)的最重要已經(jīng)說完了媚污。下面簡單說一下一些方法的作用
- firstEntry() 返回Map中最小的key
- higherEntry(Object key ) 返回該Map中位于key后一位的key-value
- lowerEntry(Object key ) 返回該Map中唯一key前一位的key-value
- tailMap(Object key , boolean inclusive) 返回該Map的子Map
少年聽雨歌樓上,紅燭昏羅帳廷雅。
壯年聽雨客舟中耗美,江闊云低,斷雁叫西風(fēng)航缀。
感謝支持商架!
---起個(gè)名忒難