/** * Replaces all linked nodes in bin at index for given hash unless * table is too small, in which case resizes instead. */ final void treeifyBin(Node<K,V>[] tab, int hash) { int n, index; Node<K,V> e; // 當前tab容量為空,或者tab數組大小小於64的情況下,不會進行紅黑樹轉換,使用的 resize()代替 // resize會增加一倍的容量(在當前的基礎上),可以看看我這邊之前resize()解讀的文章 if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) resize(); else if ((e = tab[index = (n - 1) & hash]) != null) { TreeNode<K,V> hd = null, tl = null; do { // 構建節點 TreeNode<K,V> p = replacementTreeNode(e, null); if (tl == null) hd = p; else { p.prev = tl; tl.next = p; } tl = p; } while ((e = e.next) != null); // 樹根節點賦值給數組 if ((tab[index] = hd) != null) // 構造紅黑樹 hd.treeify(tab); } } // For treeifyBin TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) { return new TreeNode<>(p.hash, p.key, p.value, next); } /** * Forms tree of the nodes linked from this node. * @return root of tree * 鏈表轉換成樹 */ final void treeify(Node<K,V>[] tab) { // 樹的跟節點 TreeNode<K,V> root = null; // 遍歷鏈表,x指向當前節點、next指向下一個節點 for (TreeNode<K,V> x = this, next; x != null; x = next) { // 下一個節點 next = (TreeNode<K,V>)x.next; // 當前節點的左右節點為空 x.left = x.right = null; // 如果還沒有根節點 if (root == null) { // 根節點的父節點為null x.parent = null; // 根節點為黑色 x.red = false; root = x; } else { // 根節點已存在,獲取當前節點的key K k = x.key; // 當前節點的hash int h = x.hash; // 當前節點key所屬的類類型 Class<?> kc = null; // 從根節點開始遍歷 for (TreeNode<K,V> p = root;;) { // dir標識遍歷的方向(左右),ph標識當前循環內當前節點的hash int dir, ph; // 當前循環內當前節點的key K pk = p.key; // 如果當前循環內的節點的hash值大於,當前節點(循環外的x節點)的hash值 if ((ph = p.hash) > h) // 當前節點x會放到當前遍歷到的節點的左側 dir = -1; else if (ph < h) // 否則是右側 dir = 1; // 如果兩個節點的hash相同 // 如果當前節點x實現了comparable接口,並且當前遍歷到的節點和當前節點x是相同class的實例,那么通過comparable在比較一次 // 如果還是相等,通過tieBreakOrder進行比較,如果identityHashCode還是一樣,最終就會放到左側 else if ((kc == null && (kc = comparableClassFor(k)) == null) || // 根據comparable進行比較 (dir = compareComparables(kc, k, pk)) == 0) dir = tieBreakOrder(k, pk); // 保存當前遍歷到的節點 TreeNode<K,V> xp = p; /* * 如果dir 小於等於0 :當前鏈表節點一定放置在當前樹節點的左側,但不一定是該樹節點的左孩子,也可能是左孩子的右孩子 或者 更深層次的節點。 * 如果dir 大於0 :當前鏈表節點一定放置在當前樹節點的右側,但不一定是該樹節點的右孩子,也可能是右孩子的左孩子 或者 更深層次的節點。 * 如果當前樹節點不是葉子節點,那么最終會以當前樹節點的左孩子或者右孩子 為 起始節點 再從GOTO1 處開始 重新尋找自己(當前鏈表節點)的位置 * 如果當前樹節點就是葉子節點,那么根據dir的值,就可以把當前鏈表節點掛載到當前樹節點的左或者右側了。 * 掛載之后,還需要重新把樹進行平衡。平衡之后,就可以針對下一個鏈表節點進行處理了。 */ if ((p = (dir <= 0) ? p.left : p.right) == null) { // 當前節點的父節點是遍歷到的節點 x.parent = xp; if (dir <= 0) // 作為遍歷到的節點的左孩子 xp.left = x; else // 作為右孩子 xp.right = x; // 執行紅黑樹新增節點時的平衡操作 root = balanceInsertion(root, x); break; } } } } // 把所有的鏈表節點都遍歷完之后,最終構造出來的樹可能經歷多個平衡操作,根節點目前到底是鏈表的哪一個節點是不確定的 // 因為我們要基於樹來做查找,所以就應該把 tab[N] 得到的對象一定根節點對象,而目前只是鏈表的第一個節點對象,所以要做相應的處理。 moveRootToFront(tab, root); } /** * Returns x's Class if it is of the form "class C implements * Comparable<C>", else null. 當x的類型為X,且X直接實現了Comparable接口(比較類型必須為X類本身)時,返回x的運行時類型;否則返回null。 */ static Class<?> comparableClassFor(Object x) { // 判斷是否是實現了Comparable接口 if (x instanceof Comparable) { Class<?> c; Type[] ts, as; Type t; ParameterizedType p; if ((c = x.getClass()) == String.class) // bypass checks // 如果是String類型,直接返回String.class return c; // 判斷是否有直接實現的接口 if ((ts = c.getGenericInterfaces()) != null) { for (int i = 0; i < ts.length; ++i) { if (((t = ts[i]) instanceof ParameterizedType) && // 該接口實現了泛型 // 獲取接口不帶參數部分的類型對象,也就是去掉了泛型參數部分的類型對象。 ((p = (ParameterizedType)t).getRawType() == // 該類型是Comparable Comparable.class) && // 獲取泛型參數數組 (as = p.getActualTypeArguments()) != null && // 只有一個泛型參數,且該實現類型是該類型本身 as.length == 1 && as[0] == c) // type arg is c return c; } } } return null; } /** * 關於紅黑樹的特性: * 性質1.節點是紅色或黑色。 * 性質2.根節點是黑色。 * 性質3.每個葉節點(NIL節點,空節點)是黑色的。 * 性質4.每個紅色節點的兩個子節點都是黑色。(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點) * 性質5.從任一節點到其每個葉子的路徑上包含的黑色節點數量都相同。 * root 當前根節,x 新插入的節點,返回重新平衡后的根節點 */ static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,TreeNode<K,V> x) { // 新插入的節點標識為紅色 x.red = true; // xp:當前節點的父節點、xpp:爺爺節點、xppl:左叔叔節點、xppr:右叔叔節點 for (TreeNode<K,V> xp, xpp, xppl, xppr;;) { // 如果父節點為空、說明當前節點就是根節點,那么把當前節點標為黑色,返回當前節點 if ((xp = x.parent) == null) {//L1 x.red = false; return x; } // 如果父節點為黑色 或者(父節點為紅色 但是 爺爺節點為空) // xp就是根節點時,並且當前樹的節點數小於等於3個時,會出現這種情況 else if (!xp.red || (xpp = xp.parent) == null) // L2 return root; // 如果父節點是爺爺節點的左孩子 -- 樹的左子樹 if (xp == (xppl = xpp.left)) { // L3 // 如果右叔叔節點不為空,且是紅色 if ((xppr = xpp.right) != null && xppr.red) { // L3_1 // 右叔叔置為黑色 xppr.red = false; // 父節點置為黑色 xp.red = false; // 爺爺節點設置為紅色 xpp.red = true; // 下一輪循環,當前節點變為爺爺節點開始 x = xpp; } // 如果右叔叔為空,或者為黑色 else {// L3_2 // 如果當前節點是父節點的右孩子 if (x == xp.right) { //L3_2_1 // 父節點左旋 root = rotateLeft(root, x = xp); xpp = (xp = x.parent) == null ? null : xp.parent; } // 如果父節點不為空 if (xp != null) { // L3_2_2 // 父節點 置為黑色 xp.red = false; // 爺爺節點不為空 if (xpp != null) { // 爺爺節點置為 紅色 xpp.red = true; // 爺爺節點右旋 root = rotateRight(root, xpp); } } } } // 如果父節點是爺爺節點的右孩子 -- 樹的右子樹 else { // L4 // 如果左叔叔是紅色,和上面的"右叔叔節點不為空,且是紅色"的情況類似 if (xppl != null && xppl.red) { // L4_1 // 左叔叔置為 黑色 xppl.red = false; // 父節點置為黑色 xp.red = false; // 爺爺置為紅色 xpp.red = true; // 下一輪循環,當前節點變為爺爺節點開始 x = xpp; } // 如果左叔叔為空或者是黑色 else { // L4_2 // 如果當前節點是個左孩子 if (x == xp.left) { // L4_2_1 // 針對父節點做右旋 root = rotateRight(root, x = xp); xpp = (xp = x.parent) == null ? null : xp.parent; } // 如果父節點不為空 if (xp != null) { //L4_2_4 // 父節點置為黑色 xp.red = false; // 如果爺爺節點不為空 if (xpp != null) { // 爺爺節點置為紅色 xpp.red = true; // 針對爺爺節點做左旋 root = rotateLeft(root, xpp); } } } } } } /** * 節點左旋 * root 根節點 * p 要左旋的節點 */ static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root, TreeNode<K,V> p) { TreeNode<K,V> r, pp, rl; if (p != null && (r = p.right) != null) { // 要左旋的節點以及要左旋的節點的右孩子不為空 if ((rl = p.right = r.left) != null) // 要左旋的節點的右孩子的左節點 賦給 要左旋的節點的右孩子 節點為:rl rl.parent = p; // 設置rl和要左旋的節點的父子關系【之前只是爹認了孩子,孩子還沒有答應,這一步孩子也認了爹】 // 將要左旋的節點的右孩子的父節點 指向 要左旋的節點的父節點,相當於右孩子提升了一層, // 此時如果父節點為空, 說明r 已經是頂層節點了,應該作為root 並且標為黑色 if ((pp = r.parent = p.parent) == null) (root = r).red = false; else if (pp.left == p) // 如果父節點不為空 並且 要左旋的節點是個左孩子 pp.left = r; // 設置r和父節點的父子關系【之前只是孩子認了爹,爹還沒有答應,這一步爹也認了孩子】 else // 要左旋的節點是個右孩子 pp.right = r; r.left = p; // 要左旋的節點 作為 他的右孩子的左節點 p.parent = r; // 要左旋的節點的右孩子 作為 他的父節點 } return root; // 返回根節點 } /** * 節點右旋 * root 根節點 * p 要右旋的節點 */ static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root, TreeNode<K,V> p) { TreeNode<K,V> l, pp, lr; if (p != null && (l = p.left) != null) { // 要右旋的節點不為空以及要右旋的節點的左孩子不為空 if ((lr = p.left = l.right) != null) // 要右旋的節點的左孩子的右節點 賦給 要右旋節點的左孩子 節點為:lr lr.parent = p; // 設置lr和要右旋的節點的父子關系【之前只是爹認了孩子,孩子還沒有答應,這一步孩子也認了爹】 // 將要右旋的節點的左孩子的父節點 指向 要右旋的節點的父節點,相當於左孩子提升了一層, // 此時如果父節點為空, 說明l 已經是頂層節點了,應該作為root 並且標為黑色 if ((pp = l.parent = p.parent) == null) (root = l).red = false; else if (pp.right == p) // 如果父節點不為空 並且 要右旋的節點是個右孩子 pp.right = l; // 設置l和父節點的父子關系【之前只是孩子認了爹,爹還沒有答應,這一步爹也認了孩子】 else // 要右旋的節點是個左孩子 pp.left = l; // 同上 l.right = p; // 要右旋的節點 作為 他左孩子的右節點 p.parent = l; // 要右旋的節點的父節點 指向 他的左孩子 } return root; }
紅黑樹轉換圖例:
1、無旋轉
2、有旋轉
參考:https://blog.csdn.net/qpzkobe/article/details/79533237
參考:https://blog.csdn.net/weixin_42340670/article/details/80531795
參考:https://blog.csdn.net/weixin_42340670/article/details/80531795