實現原理:紅黑樹
什么是紅黑樹(數據來自百度百科):
紅黑樹(自平衡二叉樹)是每個節點都帶有顏色屬性的二叉查找樹,顏色或紅色或黑色。在二叉查找樹強制一般要求以外,對於任何有效的紅黑樹我們增加了如下的額外要求:
性質1. 節點是紅色或黑色。性質2. 根節點是黑色。性質3. 每個葉節點(NIL節點,空節點)是黑色的。性質4. 每個紅色節點的兩個子節點都是黑色。(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點)性質5. 從任一節點到其每個葉子的所有路徑都包含相同數目的黑色節點。
這些約束強制了紅黑樹的關鍵性質:
從根到葉子的最長的可能路徑不多於最短的可能路徑的兩倍長。結果是這個樹大致上是平衡的。因為操作比如插入、刪除和查找某個值的最壞情況時間都要求與樹的高度成比例,這個在高度上的理論上限允許紅黑樹在最壞情況下都是高效的,而不同於普通的二叉查找樹。

思路:
1.創建空比較器,空Entry引用,entry個數默認為0
//比較器,因為TreeMap是有序的,通過comparator接口我們可以對TreeMap的內部排序進行精密的控制 private final Comparator<? super K> comparator; //TreeMap紅-黑節點,為TreeMap的內部類 private transient Entry<K,V> root = null; private transient int size = 0; private static final boolean RED = false; private static final boolean BLACK = true;
2.四個構造器,全部使用比較器,比較器基本容器/單位?
public TreeMap() { comparator = null; } public TreeMap(Comparator<? super K> comparator) { this.comparator = comparator; } public TreeMap(Map<? extends K, ? extends V> m) { comparator = null; putAll(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) { } }
3.Entry類
static final class Entry<K,V> implements Map.Entry<K,V> { K key; V value; Entry<K,V> left; Entry<K,V> right; Entry<K,V> parent; boolean color = BLACK; /** * Make a new cell with given key, value, and parent, and with * {@code null} child links, and BLACK color. */ Entry(K key, V value, Entry<K,V> parent) { this.key = key; this.value = value; this.parent = parent; } }
4.put方法
兩個點:
構建一個有序二叉樹
平衡二叉樹
1、以根節點為初始節點進行檢索。
2、與當前節點進行比對,若新增節點值較大,則以當前節點的右子節點作為新的當前節點。
否則以當前節點的左子節點作為新的當前節點。
3、循環遞歸2步驟知道檢索出合適的葉子節點為止。
4、將新增節點與3步驟中找到的節點進行比對,如果新增節點較大,則添加為右子節點;否則添加為左子節點。
1.root為空
新增根節點
return
2.root不為空,比較器不為空,
在root左右插入節點后 繼續
或者替換root值后 return
3.root不為空,比較器為空
key為空,拋出空指針
在root左右插入節點后 繼續
或者替換root值后 return
public V put(K key, V value) { //用t表示二叉樹的當前節點 Entry<K,V> t = root; //t為null表示一個空樹,即TreeMap中沒有任何元素,直接插入 if (t == null) { //比較key值,個人覺得這句代碼沒有任何意義,空樹還需要比較、排序? compare(key, key); // type (and possibly null) check //將新的key-value鍵值對創建為一個Entry節點,並將該節點賦予給root root = new Entry<>(key, value, null); //容器的size = 1,表示TreeMap集合中存在一個元素 size = 1; modCount++; return null; } //cmp表示key排序的返回結果 int cmp; //父節點 Entry<K,V> parent; // split comparator and comparable paths //指定的排序算法 Comparator<? super K> cpr = comparator; //非空比較器時 ////如果cpr不為空,則采用既定的排序算法進行創建TreeMap集合 if (cpr != null) { do { //parent指向上次循環后的t parent = t; //比較新增節點的key和當前節點key的大小 cmp = cpr.compare(key, t.key); //cmp返回值小於0,表示新增節點的key小於當前節點的key, //則以當前節點的左子節點作為新的當前節點 if (cmp < 0) t = t.left; //cmp返回值大於0,表示新增節點的key大於當前節點的key, //則以當前節點的右子節點作為新的當前節點 else if (cmp > 0) t = t.right; else //cmp返回值等於0,表示兩個key值相等,則新值覆蓋舊值,並返回新值 return t.setValue(value); } while (t != null); } else { //如果cpr為空,則采用默認的排序算法進行創建TreeMap集合 //key不能為空 if (key == null) throw new NullPointerException(); /* 下面處理過程和上面一樣 */ @SuppressWarnings("unchecked") 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); } //將新增節點當做parent的子節點 Entry<K,V> e = new Entry<>(key, value, parent); //如果新增節點的key小於parent的key,則當做左子節點 if (cmp < 0) parent.left = e; else //如果新增節點的key大於parent的key,則當做右子節點 parent.right = e; /** * 上面已經完成了排序二叉樹的的構建,將新增節點插入該樹中的合適位置 * 下面fixAfterInsertion()方法就是對這棵樹進行調整、平衡,具體過程參考上面的五種情況 */ fixAfterInsertion(e); size++; modCount++; return null; }
上面的do{}代碼僅僅是排序二叉樹的核心算法,真正的實現紅黑樹依靠的是fixAfterInsertion(e)方法
紅黑樹是一棵平衡排序二叉樹,普通的排序二叉樹可能會出現失衡的情況,而fixAfterInsertion就是調
整結構實現紅黑樹 : 左旋、右旋、着色三個基本操作
/** From CLR */ private void fixAfterInsertion(Entry<K,V> x) { //新節點着色 x.color = RED; //循環到新節點 不為空且不是根且根的節點不是紅色 while (x != null && x != root && x.parent.color == RED) { //如果X的父節點(假設為:P)是其父節點的父節點(假設為:G)的左節點 if (parentOf(x) == leftOf(parentOf(parentOf(x)))) { //獲取父節點的父節點的右節點y Entry<K,V> y = rightOf(parentOf(parentOf(x))); // G // / \ // p y // / // 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); //以X的父節點的父節點(G)為中心左旋轉 rotateLeft(x); } setColor(parentOf(x), BLACK); setColor(parentOf(parentOf(x)), RED); rotateRight(parentOf(parentOf(x))); } } else { //如果X的父節點(假設為:P)是其父節點的父節點(假設為:G)的右節點 // G // / \ // y P // \ // x // 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); //以X的父節點的父節點(G)為中心右旋轉 rotateRight(x); } setColor(parentOf(x), BLACK); setColor(parentOf(parentOf(x)), RED); rotateLeft(parentOf(parentOf(x))); } } } root.color = BLACK; }
右旋方法(左旋類似)
/** From CLR */ private void rotateRight(Entry<K,V> p) { if (p != null) { // p // / // L //將L設置為P的左子樹 Entry<K,V> l = p.left; //將L的右子樹設置為P的左子樹 p.left = l.right; //若L的右子樹不為空,則將P設置L的右子樹的父節點 if (l.right != null) l.right.parent = p; //將P的父節點設置為L的父節點 l.parent = p.parent; //如果P的父節點為空,則將L設置根節點 if (p.parent == null) root = l; //若P為其父節點的右子樹,則將L設置為P的父節點的右子樹 else if (p.parent.right == p) p.parent.right = l; //否則將L設置為P的父節點的左子樹 else p.parent.left = l; //將P設置為L的右子樹 l.right = p; //將L設置為P的父節點 p.parent = l; } }