精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

3分鐘讓你明白:HashMap之紅黑樹樹化過程

開發 架構
HashMap是Java程序員使用頻率最高的用于映射(鍵值對)處理的數據類型。隨著JDK(Java Developmet Kit)版本的更新,JDK1.8對HashMap底層的實現進行了優化,例如引入紅黑樹的數據結構和擴容的優化等。本文主要分析一下HashMap中紅黑樹樹化的過程。

 01 概述

HashMap是Java程序員使用頻率最高的用于映射(鍵值對)處理的數據類型。隨著JDK(Java Developmet Kit)版本的更新,JDK1.8對HashMap底層的實現進行了優化,例如引入紅黑樹的數據結構和擴容的優化等。本文主要分析一下HashMap中紅黑樹樹化的過程。

[[278900]]

02 紅黑樹(red black tree)

一個節點標記為紅色或者黑色。 根是黑色的。 如果一個節點是紅色的,那么它的子節點必須是黑色的(這就是為什么叫紅黑樹)。 一個節點到到一個null引用的每一條路徑必須包含相同數目的黑色節點(所以紅色節點不影響)。 其實RB Tree和著名的AVL Tree有很多相同的地方,困難的地方都在于將一個新項插入到樹中。了解AVL Tree的朋友應該都知道為了維持樹的高度必須在插入一個新的項后必須在樹的結構上進行改變,這里主要是通過旋轉,當然在RB Tree中原理也是如此。

03 兩種旋轉和一種典型的變換

旋轉的方向:

3分鐘讓你明白:HashMap之紅黑樹樹化過程

變換過程:

3分鐘讓你明白:HashMap之紅黑樹樹化過程

互相關聯:

3分鐘讓你明白:HashMap之紅黑樹樹化過程

單向關聯:

3分鐘讓你明白:HashMap之紅黑樹樹化過程

代表紅色的節點:

3分鐘讓你明白:HashMap之紅黑樹樹化過程

代表黑色的節點:

3分鐘讓你明白:HashMap之紅黑樹樹化過程

代表一個不會破壞紅黑樹結構的部分,可能是節點,或者是一個子樹,總之不會破環當前樹的結構。這個部分會由于旋轉而連接到其他的節點后面,我們可以理解成由于重力原因它掉到了下面的節點上:

3分鐘讓你明白:HashMap之紅黑樹樹化過程

 

3分鐘讓你明白:HashMap之紅黑樹樹化過程

 

3分鐘讓你明白:HashMap之紅黑樹樹化過程
  • 單旋轉變換。
  • 雙旋轉變換(需要兩次反方向的單旋轉)。
  • 當遇到兩個子幾點都為紅色的話執行顏色變換,因為插入 是紅色的會產生沖突。如果根節點兩邊的子節點都是紅色,兩個葉子節點變成黑色,根節點變成紅色,然后再將根節點變成黑色。

上面的圖中描述了紅黑樹中三種典型的變換,其實前兩種變換這正是AVL Tree中的兩種典型的變換。

04 幾個問題

4.1 為什么要進行旋轉?

由于P和X節點都為紅色節點這破環了紅節點下面的節點必須為黑色節點的規則。

4.2 新加入的節點總是紅色的,這是為什么呢?

因為被插入前的樹結構是構建好的,一但我們進行添加黑色的節點,無論添加在哪里都會破壞原有路徑上的黑色節點的數量平等關系,所以插入紅色節點是正確的選擇。

4.3 為什么要進行顏色變換?

正如第一種旋轉新加入的節點X破壞了紅黑樹的結構不得不進行旋轉,后面的就是旋轉后的結果,旋轉后形成新的結構,此時我們發現兩個子節點都是紅色的所以執行第三個變換特性,顏色變換,因為如果子節點是紅色的那么我們在添加的時候只能添加黑色的節點,然而添加任何黑色葉子節點都會破壞樹的第四條性質,所以要對其進行變換。當進行變換后葉子節點是紅色的而且我們默認添加的葉子節點是紅色的,所以添加到黑色節點后并不會破壞樹的第四條結構,所以這種變換很有用。

第二種雙變換中在樹的內部怎么出現的紅色的節點? 正是由于上面的顏色變換導致新顏色變換后的節點與他的父節點產生了顏色沖突。

與AVL樹相比? 比AVL樹相比優點是不用在節點類中保存一個節點高度這個變量,節省了內存。

而且紅黑樹一般不是以遞歸方式實現的而是以循環的形式實現。

一般的操作在最壞情形下花費O(logN)時間。

05 好了有了這些基本的概念讓我們去看一下HashMap中的代碼的實現

  1. final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) 
  2.  { 
  3.  Node<K, V>[] tab; 
  4.  Node<K, V> p; 
  5.  int n, i; 
  6.  if ((tab = table) == null || (n = tab.length) == 0) 
  7.  n = (tab = resize()).length; 
  8.  if ((p = tab[i = (n - 1) & hash]) == null
  9.  tab[i] = newNode(hash, key, value, null); 
  10.  else 
  11.  { 
  12.  Node<K, V> e; 
  13.  K k; 
  14.  if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) 
  15.  e = p; 
  16.  else if (p instanceof TreeNode) 
  17.  // 如果當前的bucket里面已經是紅黑樹的話,執行紅黑樹的添加操作 
  18.  e = ((TreeNode<K, V>) p).putTreeVal(this, tab, hash, key, value); 
  19.  else 
  20.  { 
  21.  for (int binCount = 0;; ++binCount) 
  22.  { 
  23.  if ((e = p.next) == null
  24.  { 
  25.  p.next = newNode(hash, key, value, null); 
  26.  // TREEIFY_THRESHOLD = 8,判斷如果當前bucket的位置鏈表長度大于8的話就將此鏈表變成紅黑樹。 
  27.  if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st 
  28.  treeifyBin(tab, hash); 
  29.  break; 
  30.  } 
  31.  if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) 
  32.  break; 
  33.  p = e; 
  34.  } 
  35.  } 
  36.  if (e != null
  37.  { // existing mapping for key 
  38.  V oldValue = e.value; 
  39.  if (!onlyIfAbsent || oldValue == null
  40.  e.value = value; 
  41.  afterNodeAccess(e); 
  42.  return oldValue; 
  43.  } 
  44.  } 
  45.  ++modCount; 
  46.  if (++size > threshold) 
  47.  resize(); 
  48.  afterNodeInsertion(evict); 
  49.  return null
  50.  } 

上面的方法通過hash計算插入的項的槽位,如果有是一樣的key則根據設置的參數是否執行覆蓋,如果相應槽位空的話直接插入,如果對應的槽位有項則判斷是紅黑樹結構還是鏈表結構的槽位,鏈表的話則順著鏈表尋找如果找到一樣的key則根據參數選擇覆蓋,沒有找到則鏈接在鏈表最后面,鏈表項的數目大于8則對其進行樹化,如果是紅黑樹結構則按照樹的添加方式添加項。

讓我們看一下treeifyBin這個方法。

  1. final void treeifyBin(Node<K, V>[] tab, int hash) 
  2.  { 
  3.  int n, index
  4.  Node<K, V> e; 
  5.  if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) 
  6.  // resize()方法這里不過多介紹,感興趣的可以去看上面的鏈接。 
  7.  resize(); 
  8.  // 通過hash求出bucket的位置。 
  9.  else if ((e = tab[index = (n - 1) & hash]) != null
  10.  { 
  11.  TreeNode<K, V> hd = null, tl = null
  12.  do 
  13.  { 
  14.  // 將每個節點包裝成TreeNode。 
  15.  TreeNode<K, V> p = replacementTreeNode(e, null); 
  16.  if (tl == null
  17.  hd = p; 
  18.  else 
  19.  { 
  20.  // 將所有TreeNode連接在一起此時只是鏈表結構。 
  21.  p.prev = tl; 
  22.  tl.next = p; 
  23.  } 
  24.  tl = p; 
  25.  } while ((e = e.next) != null); 
  26.  if ((tab[index] = hd) != null
  27.  // 對TreeNode鏈表進行樹化。 
  28.  hd.treeify(tab); 
  29.  } 
  30.  } 

找個方法所做的事情就是將剛才九個項以鏈表的方式連接在一起,然后通過它構建紅黑樹。

看代碼之前我們先了解一下TreeNode

  1. static final class TreeNode<K, V> extends LinkedHashMap.Entry<K, V> 
  2.  { 
  3.  TreeNode<K, V> parent; // red-black tree links 
  4.  TreeNode<K, V> left
  5.  TreeNode<K, V> right
  6.  TreeNode<K, V> prev; // needed to unlink next upon deletion 
  7.  boolean red; 
  8.  TreeNode(int hash, K key, V val, Node<K, V> next
  9.  { 
  10.  super(hash, key, val, next); 
  11.  } 
  12.   
  13.  final void treeify(Node<K,V>[] tab) 
  14.  { 
  15.  // ...... 
  16.  } 
  17.   
  18.  static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root, TreeNode<K,V> x) 
  19.  { 
  20.  // ...... 
  21.  } 
  22.   
  23.  static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root, TreeNode<K,V> p) 
  24.  { 
  25.  // ...... 
  26.  } 
  27.   
  28.  static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root, TreeNode<K,V> p) 
  29.  { 
  30.  // ...... 
  31.  } 
  32.   
  33.  // ......其余方法省略 
  34.  } 

可以看出出真正的維護紅黑樹結構的方法并沒有在HashMap中,全部都在TreeNode類內部。

我們看一下treeify代碼

  1. final void treeify(Node<K, V>[] tab) 
  2.  { 
  3.  TreeNode<K, V> root = null
  4.  // 以for循環的方式遍歷剛才我們創建的鏈表。 
  5.  for (TreeNode<K, V> x = this, next; x != null; x = next
  6.  { 
  7.  // next向前推進。 
  8.  next = (TreeNode<K, V>) x.next
  9.  x.left = x.right = null
  10.  // 為樹根節點賦值。 
  11.  if (root == null
  12.  { 
  13.  x.parent = null
  14.  x.red = false
  15.  root = x; 
  16.  } else 
  17.  { 
  18.  // x即為當前訪問鏈表中的項。 
  19.  K k = x.key
  20.  int h = x.hash; 
  21.  Class<?> kc = null
  22.  // 此時紅黑樹已經有了根節點,上面獲取了當前加入紅黑樹的項的key和hash值進入核心循環。 
  23.  // 這里從root開始,是以一個自頂向下的方式遍歷添加。 
  24.  // for循環沒有控制條件,由代碼內break跳出循環。 
  25.  for (TreeNode<K, V> p = root;;) 
  26.  { 
  27.  // dir:directory,比較添加項與當前樹中訪問節點的hash值判斷加入項的路徑,-1為左子樹,+1為右子樹。 
  28.  // ph:parent hash。 
  29.  int dir, ph; 
  30.  K pk = p.key
  31.  if ((ph = p.hash) > h) 
  32.  dir = -1; 
  33.  else if (ph < h) 
  34.  dir = 1; 
  35.  else if ((kc == null && (kc = comparableClassFor(k)) == null
  36.  || (dir = compareComparables(kc, k, pk)) == 0) 
  37.  dir = tieBreakOrder(k, pk); 
  38.  // xp:x parent。 
  39.  TreeNode<K, V> xp = p; 
  40.  // 找到符合x添加條件的節點。 
  41.  if ((p = (dir <= 0) ? p.left : p.right) == null
  42.  { 
  43.  x.parent = xp; 
  44.  // 如果xp的hash值大于x的hash值,將x添加在xp的左邊。 
  45.  if (dir <= 0) 
  46.  xp.left = x; 
  47.  // 反之添加在xp的右邊。 
  48.  else 
  49.  xp.right = x; 
  50.  // 維護添加后紅黑樹的紅黑結構。 
  51.  root = balanceInsertion(root, x); 
  52.   
  53.  // 跳出循環當前鏈表中的項成功的添加到了紅黑樹中。 
  54.  break; 
  55.  } 
  56.  } 
  57.  } 
  58.  } 
  59.  // Ensures that the given root is the first node of its bin,自己翻譯一下。 
  60.  moveRootToFront(tab, root); 
  61.  } 

第一次循環會將鏈表中的首節點作為紅黑樹的根,而后的循環會將鏈表中的的項通過比較hash值然后連接到相應樹節點的左邊或者右邊,插入可能會破壞樹的結構所以接著執行balanceInsertion。

我們看balanceInsertion

  1. static <K, V> TreeNode<K, V> balanceInsertion(TreeNode<K, V> root, TreeNode<K, V> x) 
  2.  { 
  3.  // 正如開頭所說,新加入樹節點默認都是紅色的,不會破壞樹的結構。 
  4.  x.red = true
  5.  // 這些變量名不是作者隨便定義的都是有意義的。 
  6.  // xp:x parent,代表x的父節點。 
  7.  // xpp:x parent parent,代表x的祖父節點 
  8.  // xppl:x parent parent left,代表x的祖父的左節點。 
  9.  // xppr:x parent parent right,代表x的祖父的右節點。 
  10.  for (TreeNode<K, V> xp, xpp, xppl, xppr;;) 
  11.  { 
  12.  // 如果x的父節點為null說明只有一個節點,該節點為根節點,根節點為黑色,red = false。 
  13.  if ((xp = x.parent) == null
  14.  { 
  15.  x.red = false
  16.  return x; 
  17.  }  
  18.  // 進入else說明不是根節點。 
  19.  // 如果父節點是黑色,那么大吉大利(今晚吃雞),紅色的x節點可以直接添加到黑色節點后面,返回根就行了不需要任何多余的操作。 
  20.  // 如果父節點是紅色的,但祖父節點為空的話也可以直接返回根此時父節點就是根節點,因為根必須是黑色的,添加在后面沒有任何問題。 
  21.  else if (!xp.red || (xpp = xp.parent) == null
  22.  return root; 
  23.   
  24.  // 一旦我們進入到這里就說明了兩件是情 
  25.  // 1.x的父節點xp是紅色的,這樣就遇到兩個紅色節點相連的問題,所以必須經過旋轉變換。 
  26.  // 2.x的祖父節點xpp不為空。 
  27.   
  28.  // 判斷如果父節點是否是祖父節點的左節點 
  29.  if (xp == (xppl = xpp.left)) 
  30.  { 
  31.  // 父節點xp是祖父的左節點xppr 
  32.  // 判斷祖父節點的右節點不為空并且是否是紅色的 
  33.  // 此時xpp的左右節點都是紅的,所以直接進行上面所說的第三種變換,將兩個子節點變成黑色,將xpp變成紅色,然后將紅色節點x順利的添加到了xp的后面。 
  34.  // 這里大家有疑問為什么將x = xpp? 
  35.  // 這是由于將xpp變成紅色以后可能與xpp的父節點發生兩個相連紅色節點的沖突,這就又構成了第二種旋轉變換,所以必須從底向上的進行變換,直到根。 
  36.  // 所以令x = xpp,然后進行下下一層循環,接著往上走。 
  37.  if ((xppr = xpp.right) != null && xppr.red) 
  38.  { 
  39.  xppr.red = false
  40.  xp.red = false
  41.  xpp.red = true
  42.  x = xpp; 
  43.  } 
  44.  // 進入到這個else里面說明。 
  45.  // 父節點xp是祖父的左節點xppr。 
  46.  // 祖父節點xpp的右節點xppr是黑色節點或者為空,默認規定空節點也是黑色的。 
  47.  // 下面要判斷x是xp的左節點還是右節點。 
  48.  else 
  49.  { 
  50.  // x是xp的右節點,此時的結構是:xpp左->xp右->x。這明顯是第二中變換需要進行兩次旋轉,這里先進行一次旋轉。 
  51.  // 下面是第一次旋轉。 
  52.  if (x == xp.right
  53.  { 
  54.  root = rotateLeft(root, x = xp); 
  55.  xpp = (xp = x.parent) == null ? null : xp.parent; 
  56.  } 
  57.  // 針對本身就是xpp左->xp左->x的結構或者由于上面的旋轉造成的這種結構進行一次旋轉。 
  58.  if (xp != null
  59.  { 
  60.  xp.red = false
  61.  if (xpp != null
  62.  { 
  63.  xpp.red = true
  64.  root = rotateRight(root, xpp); 
  65.  } 
  66.  } 
  67.  } 
  68.  }  
  69.  // 這里的分析方式和前面的相對稱只不過全部在右測不再重復分析。 
  70.  else 
  71.  { 
  72.  if (xppl != null && xppl.red) 
  73.  { 
  74.  xppl.red = false
  75.  xp.red = false
  76.  xpp.red = true
  77.  x = xpp; 
  78.  } else 
  79.  { 
  80.  if (x == xp.left
  81.  { 
  82.  root = rotateRight(root, x = xp); 
  83.  xpp = (xp = x.parent) == null ? null : xp.parent; 
  84.  } 
  85.  if (xp != null
  86.  { 
  87.  xp.red = false
  88.  if (xpp != null
  89.  { 
  90.  xpp.red = true
  91.  root = rotateLeft(root, xpp); 
  92.  } 
  93.  } 
  94.  } 
  95.  } 
  96.  } 
  97.  } 

如果您的聯想能力很強的話估計到這里應該已經理解這集中變換的主要的關系。

下面簡述一下前面的兩種種幸運的情況

x本身為根節點返回x。 x的父節點為黑色或者x的父節點是根節點直接返回不需要變換。 如若上述兩個條件不滿足的話,就要進行變換了,允許我再貼一點代碼......沒有代碼分析起來很困難。

06 顏色變換

  1. if (xp == (xppl = xpp.left)) 
  2.  { 
  3.  if ((xppr = xpp.right) != null && xppr.red) 
  4.  { 
  5.  xppr.red = false
  6.  xp.red = false
  7.  xpp.red = true
  8.  x = xpp; 
  9.  } 

這里是一個典型的一個黑色節點的兩個子節點都是紅色的所以要進行顏色變換,因為插入的都是紅色節點,當檢測到祖父節點的左右子節點都是紅色的時候兩個紅色就產生了沖突,所以先將節點進行這種顏色變換,將祖父節點變成紅色,然后將祖父的兩個子節點變成黑色,這樣我們插入的紅色節點就不會違背紅黑樹的規則了。

3分鐘讓你明白:HashMap之紅黑樹樹化過程

這里有人會有疑問,如果祖父節點是根節點呢,那樣的話祖父節點也會變成黑色,因為每次循環進行插入平衡的操作當進行這種顏色變換之后都會將插入節點的引用指向祖父節點,當進行下一輪循環的時候會優先檢測當前節點是否是根節點,如果是根節點那就將顏色變成黑色,下面看圖:

當將節點指向祖父節點進行下一輪循環時:

3分鐘讓你明白:HashMap之紅黑樹樹化過程

07 兩個核心旋轉(左旋轉和右旋轉)

  1. // 一旦我們進入到這里就說明了兩件是情 
  2.  // 1.x的父節點xp是紅色的,這樣就遇到兩個紅色節點相連的問題,所以必須經過旋轉變換。 
  3.  // 2.x的祖父節點xpp不為空。 
  4.   
  5.  // 判斷如果父節點是否是祖父節點的左節點 
  6.  if (xp == (xppl = xpp.left)) 
  7.  { 
  8.  if ((xppr = xpp.right) != null && xppr.red) 
  9.  {// ...... 
  10.  } 
  11.  // 進入到這個else里面說明。 
  12.  // 父節點xp是祖父的左節點xppr。 
  13.  // 祖父節點xpp的右節點xppr是黑色節點或者為空,默認規定空節點也是黑色的。 
  14.  // 下面要判斷x是xp的左節點還是右節點。 
  15.  else 
  16.  { 
  17.  // x是xp的右節點,此時的結構是:xpp左->xp右->x。這明顯是第二中變換需要進行兩次旋轉,這里先進行一次旋轉。 
  18.  // 下面是第一次旋轉。 
  19.  if (x == xp.right
  20.  { 
  21.  root = rotateLeft(root, x = xp); 
  22.  xpp = (xp = x.parent) == null ? null : xp.parent; 
  23.  } 
  24.  // 針對本身就是xpp左->xp左->x的結構或者由于上面的旋轉造成的這種結構進行一次旋轉。 
  25.  if (xp != null
  26.  { 
  27.  xp.red = false
  28.  if (xpp != null
  29.  { 
  30.  xpp.red = true
  31.  root = rotateRight(root, xpp); 
  32.  } 
  33.  } 
  34.  } 
  35.  }  

顏色變換完成后進入下面的else塊

我們已知xp是xpp的左節點,首先判斷了x是xp的左節點還是右節點,如果是右節點的話構成了下面的結構。

3分鐘讓你明白:HashMap之紅黑樹樹化過程

這中結構需要進行雙旋轉,首先先進行一次向左旋轉。

7.1 左旋轉

  1.  1 static <K, V> TreeNode<K, V> rotateLeft(TreeNode<K, V> root, TreeNode<K, V> p) 
  2.  2 { 
  3.  3 // r:right,右節點。 
  4.  4 // pp:parent parent,父節點的父節點。 
  5.  5 // rl:right left,右節點的左節點。 
  6.  6 TreeNode<K, V> r, pp, rl; 
  7.  7 if (p != null && (r = p.right) != null
  8.  8 { 
  9.  9 if ((rl = p.right = r.left) != null
  10. 10 rl.parent = p; 
  11. 11 if ((pp = r.parent = p.parent) == null
  12. 12 (root = r).red = false
  13. 13 else if (pp.left == p) 
  14. 14 pp.left = r; 
  15. 15 else 
  16. 16 pp.right = r; 
  17. 17 r.left = p; 
  18. 18 p.parent = r; 
  19. 19 } 
  20. 20 return root; 
  21. 21 } 

1.剛進入方法時,狀態如下圖。(RL可能是空的)

3分鐘讓你明白:HashMap之紅黑樹樹化過程

2.進入了if塊后執行到第10行后。

  1. 9 if ((rl = p.right = r.left) != null
  2. 10 rl.parent = p; 
3分鐘讓你明白:HashMap之紅黑樹樹化過程

此時如果9行的條件符合的話執行10行RL指向P,如果RL為null的話,P的右節點指向null。

3.接著看11和12行代碼。

  1. 11 if ((pp = r.parent = p.parent) == null
  2. 12 (root = r).red = false

首先我們看11行if里面的賦值語句所造成的影響。

3分鐘讓你明白:HashMap之紅黑樹樹化過程

在if里面的表達式不管符不符合條件()內的內容都會執行。

如果符合條件的話會執行12行的代碼,變成了下面的結果。

3分鐘讓你明白:HashMap之紅黑樹樹化過程

由于PP為空所以只剩下這三個。

4.如果11行的條件為假的話,執行完11行()內的表達式后執行13行

  1. 13 else if (pp.left == p) 
  2. 14 pp.left = r; 
3分鐘讓你明白:HashMap之紅黑樹樹化過程

滿足條件的話R和PP互相關聯。

5.由于進入了13和14行所以不進入15和16行的else語句。

  1. 15 else 
  2. 16 pp.right = r; 

6.看17和18行。

  1. 17 r.left = p; 
  2. 18 p.parent = r; 
3分鐘讓你明白:HashMap之紅黑樹樹化過程

最終執行完了一個旋轉變成了我們開始說的第一種旋轉的形式,這個結構還需要向右旋轉一次。

  1. if (x == xp.right
  2.  { 
  3.  root = rotateLeft(root, x = xp); 
  4.  xpp = (xp = x.parent) == null ? null : xp.parent; 
  5.  } 
  6. xpp = (xp = x.parent) == null ? null : xp.parent; 

執行完上面的代碼,旋轉后調整x,xp,和xpp的關系得到下圖。

3分鐘讓你明白:HashMap之紅黑樹樹化過程

7.2 右旋轉

  1. if (xp != null
  2. xp.red = false
  3. if (xpp != null
  4. xpp.red = true
  5. root = rotateLeft(root, xpp); 

1.首先讓XP變成黑色。

3分鐘讓你明白:HashMap之紅黑樹樹化過程

2.如果XPP不為空的話變成紅色。

3分鐘讓你明白:HashMap之紅黑樹樹化過程

由于我們在rotateLeft(root, xpp),傳進來的是XXP所以下面的的旋轉中實際上就是對XP和XXP執行了一次與上面的方向相反其他完全相同的旋轉。

接著我們看向右旋轉的代碼

  1. static <K, V> TreeNode<K, V> rotateRight(TreeNode<K, V> root, TreeNode<K, V> p) 
  2.  { 
  3.  // l:left,左節點。 
  4.  // pp:parent parent,父節點的父節點。 
  5.  // lr:left right,左節點的右節點。 
  6.  TreeNode<K, V> l, pp, lr; 
  7.  if (p != null && (l = p.left) != null
  8.  { 
  9.  if ((lr = p.left = l.right) != null
  10.  lr.parent = p; 
  11.  if ((pp = l.parent = p.parent) == null
  12.  (root = l).red = false
  13.  else if (pp.right == p) 
  14.  pp.right = l; 
  15.  else 
  16.  pp.left = l; 
  17.  l.right = p; 
  18.  p.parent = l; 
  19.  } 
  20.  return root; 
  21.  } 

3.剛進來的時候結構是這個樣子。

3分鐘讓你明白:HashMap之紅黑樹樹化過程

在這里的P就是剛才傳進來的XPP。

4.這里我們認為LR是存在的,其實這個不影響主要的旋轉,為空就指向null唄,直接執行完9和10行。

  1. 9 if ((lr = p.left = l.right) != null
  2. 0 lr.parent = p; 
3分鐘讓你明白:HashMap之紅黑樹樹化過程

5.在這里我們假使PP是存在的,直接執行完11的表達式不再執行12行。(不再分析不存在的情況)。

  1. 11 if ((pp = l.parent = p.parent) == null
  2. 12 (root = l).red = false
3分鐘讓你明白:HashMap之紅黑樹樹化過程

6.由于11行的條件不符合,現在直接執行13行的表達式,不符合執行15行else,執行16行。

  1. 15 else 
  2. 16 pp.left = l; 
3分鐘讓你明白:HashMap之紅黑樹樹化過程

7.最后執行層17和18行。

  1. 17 l.right = p; 
  2. 18 p.parent = l; 
3分鐘讓你明白:HashMap之紅黑樹樹化過程

最終完成兩次的旋轉。

08 疑問?

大家可能覺得和剛才接不上其實是這樣的,剛才在右旋轉前的時候的圖像是這個樣的。

3分鐘讓你明白:HashMap之紅黑樹樹化過程

因為我們傳進來的是XPP,所以結合上一次的向左旋轉我們在向右旋轉的時候看到全圖應該是這個樣子的。(注:XPPP可能是XPP的左父節點也可能是右父節點這里不影響,而且可以是任意顏色)

3分鐘讓你明白:HashMap之紅黑樹樹化過程

現在知道為什么XPPP可以是任意顏色的了吧,因為旋轉過后X是黑色的即便XPPP是紅色,此時我們又可以對兩個紅色的子節點進行顏色變換了,變換后X和XPPP有發生了顏色沖突,接著進行旋轉直到根。

  1. static <K, V> TreeNode<K, V> balanceInsertion(TreeNode<K, V> root, TreeNode<K, V> x) 
  2.  { 
  3.  x.red = true
  4.  for (TreeNode<K, V> xp, xpp, xppl, xppr;;) 
  5.  { 
  6.  if ((xp = x.parent) == null
  7.  { 
  8.  x.red = false
  9.  return x; 
  10.  } 
  11.  else if (!xp.red || (xpp = xp.parent) == null
  12.  return root; 
  13.  if (xp == (xppl = xpp.left)) 
  14.  { 
  15.  // 插入位置父節點在祖父節點的左邊。 
  16.  } 
  17.  else 
  18.  { 
  19.  // 插入位置父節點在祖父節點的右邊。 
  20.  } 
  21.  } 
  22.  } 

我們值分析了插入位置父節點在祖父節點的左邊的情況,并沒有分析另外一面的對稱情況,其實是一樣的因為調用的都是相同的方法。

以上就是在1.8中的HashMap新引進的紅黑樹樹化的過程,與原來的鏈表相比當同一個bucket上存儲很多entry的話樹形的查找結構明顯要比鏈表線性的的效率要高。

 

責任編輯:華軒 來源: 今日頭條
相關推薦

2020-07-09 07:00:00

HashMap

2017-06-07 18:40:33

PromiseJavascript前端

2020-09-17 07:37:09

紅黑樹數據結構

2019-08-22 09:22:44

數據結構二叉搜索樹

2019-04-01 14:01:13

B+樹索引哈希索引算法

2021-11-07 23:46:32

MySQLSQL索引

2017-11-20 10:25:20

數據庫MySQL索引

2021-11-11 15:03:35

MySQLSQL索引

2020-11-06 08:54:43

Vue 3.0函數代碼

2019-06-05 09:42:53

Kafka App 消息隊列

2009-12-11 10:02:46

Linux內存管理

2014-11-10 12:29:52

客服網站

2020-08-19 16:36:53

HashMap紅黑樹閾值

2023-08-29 08:31:13

B+樹數據索引

2016-12-08 11:01:39

紅黑樹Java

2009-11-25 09:02:12

2009-11-05 10:56:31

WCF通訊

2020-05-06 16:41:36

紅黑樹二叉查找樹

2019-09-23 11:35:23

數據結構設計紅黑樹

2021-03-19 07:59:33

紅黑樹面試數據
點贊
收藏

51CTO技術棧公眾號

欧美美乳视频| av资源新版天堂在线| 久久精品国内一区二区三区| 久久亚洲国产精品成人av秋霞| 成人免费视频久久| 日本天堂在线观看| 国产精品自拍毛片| 欧美最猛性xxxxx亚洲精品| 国产黄色录像视频| www.亚洲一二| 欧美在线视频日韩| 久草视频这里只有精品| 国产鲁鲁视频在线观看免费| 国产电影一区二区三区| 国产精品18久久久久久首页狼| 中文乱码字幕高清一区二区| 免费看久久久| 欧美一区二区三区视频免费播放| 欧美一级免费播放| av电影在线观看一区二区三区| 国产精品一卡二| 国产99久久精品一区二区 夜夜躁日日躁| 青青操在线视频观看| 日韩精品社区| 日韩精品中文字幕在线不卡尤物| 免费观看成人在线视频| 欧美videosex性极品hd| 中文在线一区二区 | 亚洲欧美精品| 男女啪啪网站视频| 免费在线播放电影| 中文字幕不卡在线观看| 老牛影视免费一区二区| 精品国产av 无码一区二区三区| 蜜桃av一区| 高清亚洲成在人网站天堂| 国产又粗又硬又长又爽| 精品高清在线| 精品网站999www| 好男人香蕉影院| 亚洲国产高清在线观看| 欧美精品久久久久久久多人混战| 亚洲高清在线免费观看| 亚洲女同av| 同产精品九九九| 男人添女荫道口图片| 欧美野外wwwxxx| 亚洲视频一区二区在线| 综合久久国产| 久草中文在线观看| 国产精品国产三级国产专播品爱网| 欧美日韩精品中文字幕一区二区| 天堂网在线资源| 不卡一二三区首页| 国产一区二区精品在线| 日日夜夜精品免费| 97se亚洲国产综合自在线| 国产精品自拍首页| 色窝窝无码一区二区三区| 成人免费视频视频在线观看免费| 国产99在线播放| 神马久久久久久久久久| av不卡在线播放| 欧美成人第一区| 久草视频在线看| 久久精品欧美一区二区三区麻豆| 日本高清视频一区二区三区| 岛国最新视频免费在线观看| 国产精品网站一区| 中文一区一区三区免费| 亚洲妇熟xxxx妇色黄| 亚洲综合在线五月| 我的公把我弄高潮了视频| 捆绑调教日本一区二区三区| 欧美性猛交xxxx偷拍洗澡| caoporn超碰97| 欧美天堂一区二区| 91精品国产91久久久久久一区二区 | 免费黄色a级片| 牛牛视频精品一区二区不卡| 亚洲免费视频一区二区| av电影在线不卡| 国产精品成人a在线观看| 久久亚洲国产精品| 国产69精品久久久久久久久久| 午夜影院日韩| 国产日韩欧美影视| 亚洲成人精品女人久久久| www国产精品av| 亚洲精品乱码久久久久久蜜桃91| 最新黄网在线观看| 狠狠综合久久av一区二区小说| 热久久精品免费视频| 国产高清日韩| 国产视频在线一区二区| 任你操精品视频| 黄页网站一区| 国产精品久久久久久久久| 99久久亚洲精品日本无码| av在线不卡观看免费观看| 丝袜足脚交91精品| 欧美xxxx免费虐| 欧美视频完全免费看| 深夜视频在线观看| 日韩情爱电影在线观看| 97精品国产91久久久久久| 免费视频网站在线观看入口| 国产一区二区三区高清播放| 欧美精品一区在线发布| 3d玉蒲团在线观看| 日本电影亚洲天堂一区| 中文字幕99页| 91精品国产乱码久久久久久| 欧美自拍大量在线观看| 999久久久久| 中文字幕欧美激情一区| 国产av天堂无码一区二区三区| 四虎国产精品永久在线国在线 | 亚洲一区二区三区乱码aⅴ蜜桃女| 天堂中文在线观看视频| 亚洲男人的天堂网| 亚洲一级免费在线观看| 免费看成人哺乳视频网站| 欧美激情国产日韩精品一区18| 中文字幕乱码人妻二区三区| 2022国产精品视频| 日本精品久久久久久久久久| 中文成人激情娱乐网| 一区二区成人精品| 中文字幕精品三级久久久| 成人午夜私人影院| 国产成人精品免费看在线播放| 欧美成人精品三级网站| 亚洲精品99久久久久| 久久综合色综合| 国产一区二区三区不卡在线观看 | 韩国av一区| 91亚洲永久免费精品| 成人免费在线视频网| 欧美日韩亚洲一区二区| 午夜男人的天堂| 亚洲二区免费| 福利视频久久| 大香伊人久久| 亚洲精品一区二区三区福利| 久久网一区二区| 懂色av中文一区二区三区| 91传媒免费视频| 青草伊人久久| 欧美激情国产高清| 色一情一乱一乱一区91av| 亚洲电影在线免费观看| 亚洲色偷偷色噜噜狠狠99网| 亚洲精品护士| 精品日本一区二区三区| 国产欧洲在线| 亚洲欧美激情一区| 久久影视中文字幕| 国产精品毛片无遮挡高清| 中文av一区二区三区| 欧美wwwww| 51蜜桃传媒精品一区二区| 在线不卡日本v二区707| 亚洲第一页在线| 日本中文字幕在线| 久久精品亚洲国产奇米99| www.天天射.com| 天天综合网91| 成人xxxxx色| 波多野结衣视频一区二区| 亚洲男人的天堂在线播放| 国产一级免费视频| 亚洲国产激情av| 国产福利精品一区二区三区| 欧美福利网址| 久久精品国产美女| 456成人影院在线观看| 日韩在线观看免费全| 国产黄色小视频在线观看| 午夜视频在线观看一区| 一区二区三区四区免费| 久久99精品国产.久久久久久| 米仓穗香在线观看| 欧美精品密入口播放| 日韩av手机在线看| 快射av在线播放一区| 欧美成人艳星乳罩| www.com亚洲| 亚洲婷婷在线视频| 欧美丰满少妇人妻精品| 久国产精品韩国三级视频| 17c丨国产丨精品视频| 综合综合综合综合综合网| 91精品国产综合久久男男| a级片免费在线观看| 在线亚洲男人天堂| 高潮毛片7777777毛片| 91电影在线观看| 欧美被狂躁喷白浆精品| 久久久久久免费毛片精品| 女人高潮一级片| 午夜综合激情| 黄色一级视频播放| 亚洲图区在线| 99热在线国产| 成人午夜毛片| 91精品国产成人| 久久99精品久久久久久野外| 亚洲欧美在线播放| 精品人妻久久久久一区二区三区| 91福利在线播放| 日韩久久精品视频| 亚洲女人小视频在线观看| 少妇精品一区二区三区| 福利91精品一区二区三区| 亚洲免费一级视频| 亚洲欧美大片| 无码熟妇人妻av在线电影| 色无极亚洲影院| 美女视频久久| 精品欧美午夜寂寞影院| 亚洲a中文字幕| 老司机精品视频网| 日本视频久久久| 黄色在线网站噜噜噜| 欧美插天视频在线播放| 在线观看黄av| 亚洲天堂免费视频| 熟妇高潮一区二区高潮| 日韩欧美视频一区| 99国产精品久久久久久久成人| 在线视频国内一区二区| 亚洲免费在线视频观看| 婷婷夜色潮精品综合在线| 农村妇女精品一区二区| 亚洲色图视频网站| www.xx日本| 一区在线观看免费| 91免费在线看片| 亚洲国产高清在线| 四虎影视1304t| 国产精品网站一区| 韩国一级黄色录像| 国产精品久久久99| 亚洲一二三精品| 国产欧美日产一区| 国产一区二区三区视频播放| 国产精品激情偷乱一区二区∴| 黑人と日本人の交わりビデオ| 欧美国产精品久久| 欧美性生交大片| 中文字幕在线观看一区| 懂色av粉嫩av蜜臀av一区二区三区| 国产精品私人影院| 免费成人美女女在线观看| 亚洲视频一区二区在线| 久久国产精品二区| 亚洲影院理伦片| 日本在线视频免费观看| 欧美日韩激情视频| 区一区二在线观看| 欧美三区在线视频| 国产露脸国语对白在线| 日韩欧美一级精品久久| 老熟妇高潮一区二区高清视频| 亚洲福利影片在线| 欧美日韩免费做爰大片| 国产香蕉97碰碰久久人人| av在线三区| 久久综合久久美利坚合众国| 久久99亚洲网美利坚合众国| 97香蕉久久超级碰碰高清版 | 欧美激情第8页| 国产欧美日韩网站| 视频一区欧美日韩| 久久久久久蜜桃一区二区| 国产一区二区91| 男男做爰猛烈叫床爽爽小说 | 亚洲欧美久久234| 日韩片欧美片| 精品国产一区二区三区无码| 美女网站久久| 亚洲理论中文字幕| 成人av午夜电影| 丁香花五月婷婷| 一区二区三区在线视频播放| 日韩成人免费观看| 欧美日韩免费一区二区三区视频| 精品国产av 无码一区二区三区 | 免费日韩av片| 亚洲黄色av片| 91亚洲国产成人精品一区二三| www.黄色在线| 亚洲一级片在线观看| 成人免费毛片男人用品| 日韩亚洲国产中文字幕欧美| 日av在线播放| 欧美不卡视频一区发布| 暖暖成人免费视频| 99精彩视频| 欧美日韩在线网站| 免费国产黄色网址| 极品少妇一区二区| 乐播av一区二区三区| 亚洲综合一二三区| 在线观看国产一区二区三区| 亚洲国产精品99久久| 免费在线视频欧美| 久久久久久网址| 国产精品3区| 日本精品一区二区三区视频| 亚洲精品欧美| 91视频福利网| 日本一区二区三区dvd视频在线 | 精品免费二区三区三区高中清不卡| 日本女优一区| 激情五月开心婷婷| 成人精品小蝌蚪| 日韩一区二区三区四区在线| 欧美日韩一区二区三区在线| 日韩一二三四| 性欧美亚洲xxxx乳在线观看| 二区三区精品| 视频一区国产精品| 久久久久99| 亚洲av无码一区二区三区网址| 亚洲一区二区三区免费视频| 国产又大又黄的视频| 最新的欧美黄色| 三级成人在线| 日本一区二区三区免费观看| 国产精品日韩| 50一60岁老妇女毛片| 亚洲宅男天堂在线观看无病毒| 国产三级在线观看视频| 久久久精品国产一区二区| 成人1区2区| 亚洲蜜桃在线| 美女国产一区二区三区| 波多野结衣一二三四区| 在线精品亚洲一区二区不卡| 久草在现在线| 国产精品96久久久久久| 国产成人久久| 日本美女高潮视频| 中文字幕免费不卡| 亚洲系列在线观看| 精品国偷自产在线视频99| 综合久久伊人| 日本一区二区三区四区五区六区| 极品少妇一区二区三区精品视频 | 欧美特黄级在线| 欧洲一区av| 国产精品video| 久久资源中文字幕| 福利视频999| 亚洲另类中文字| 后进极品白嫩翘臀在线视频| 午夜免费在线观看精品视频| 欧美黑人巨大videos精品| 欧美a在线视频| 国产欧美日韩在线看| 一区二区小视频| 美女撒尿一区二区三区| 91久久精品无嫩草影院| 91国视频在线| 国产日韩三级在线| 国产又黄又爽视频| 欧美激情国产精品| 妖精视频一区二区三区| 最新中文字幕2018| 亚洲激情五月婷婷| 天堂中文在线看| 国产精品一区=区| 一区二区日韩欧美| 给我免费观看片在线电影的| 欧美中文字幕亚洲一区二区va在线| 成人ww免费完整版在线观看| 国产另类自拍| 免费视频最近日韩| 青娱乐av在线| 亚洲美女动态图120秒| 激情小说亚洲| www.射射射| 欧美国产亚洲另类动漫| 不卡的日韩av| 国产精品久久二区| 黑人一区二区三区四区五区| 久久精品国产亚洲av久| 6080日韩午夜伦伦午夜伦| 国产粉嫩在线观看| 亚洲图片欧洲图片日韩av| 成人一区二区三区在线观看| 国产男人搡女人免费视频| 欧美成人免费小视频| 精品精品99| 99久久人妻精品免费二区| 欧美日韩成人综合天天影院| 绿色成人影院| 无码人妻aⅴ一区二区三区日本|