<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【死磕 Java 集合】--- TreeMap源碼分析(二)

          共 419字,需瀏覽 1分鐘

           ·

          2019-07-28 23:43

          640?wx_fmt=gif&tp=webp&wxfrom=5&wx_lazy=1

          插入元素

          插入元素,如果元素在樹中存在,則替換value;如果元素不存在,則插入到對應(yīng)的位置,再平衡樹。

          public
           V put(K key, V value) {
          
          Entry
           t = root;
          
          if
           (t == 
          null
          ) {
          
          // 如果沒有根節(jié)點,直接插入到根節(jié)點
                  compare(key, key); 
          // type (and possibly null) check
                  root = 
          new
          
          Entry
          <>(key, value, 
          null
          );
                  size = 
          1
          ;
                  modCount++;
          
          return
          
          null
          ;
              }
          
          // key比較的結(jié)果
          
          int
           cmp;
          
          // 用來尋找待插入節(jié)點的父節(jié)點
          
          Entry
           parent;
          
          // 根據(jù)是否有comparator使用不同的分支
          
          Comparator
           cpr = comparator;
          
          if
           (cpr != 
          null
          ) {
          
          // 如果使用的是comparator方式,key值可以為null,只要在comparator.compare()中允許即可
          
          // 從根節(jié)點開始遍歷尋找
          
          do
           {
                      parent = t;
                      cmp = cpr.compare(key, t.key);
          
          if
           (cmp < 
          0
          )
          
          // 如果小于0從左子樹尋找
                          t = t.left;
          
          else
          
          if
           (cmp > 
          0
          )
          
          // 如果大于0從右子樹尋找
                          t = t.right;
          
          else
          
          // 如果等于0,說明插入的節(jié)點已經(jīng)存在了,直接更換其value值并返回舊值
          
          return
           t.setValue(value);
                  } 
          while
           (t != 
          null
          );
              }
          
          else
           {
          
          // 如果使用的是Comparable方式,key不能為null
          
          if
           (key == 
          null
          )
          
          throw
          
          new
          
          NullPointerException
          ();
          
          @SuppressWarnings
          (
          "unchecked"
          )
          
          Comparable
           k = (
          Comparable
          ) key;
          
          // 從根節(jié)點開始遍歷尋找
          
          do
           {
                      parent = t;
                      cmp = k.compareTo(t.key);
          
          if
           (cmp < 
          0
          )
          
          // 如果小于0從左子樹尋找
                          t = t.left;
          
          else
          
          if
           (cmp > 
          0
          )
          
          // 如果大于0從右子樹尋找
                          t = t.right;
          
          else
          
          // 如果等于0,說明插入的節(jié)點已經(jīng)存在了,直接更換其value值并返回舊值
          
          return
           t.setValue(value);
                  } 
          while
           (t != 
          null
          );
              }
          
          // 如果沒找到,那么新建一個節(jié)點,并插入到樹中
          
          Entry
           e = 
          new
          
          Entry
          <>(key, value, parent);
          
          if
           (cmp < 
          0
          )
          
          // 如果小于0插入到左子節(jié)點
                  parent.left = e;
          
          else
          
          // 如果大于0插入到右子節(jié)點
                  parent.right = e;
          
          // 插入之后的平衡
              fixAfterInsertion(e);
          
          // 元素個數(shù)加1(不需要擴容)
              size++;
          
          // 修改次數(shù)加1
              modCount++;
          
          // 如果插入了新節(jié)點返回空
          
          return
          
          null
          ;
          }
          

          插入再平衡

          插入的元素默認(rèn)都是紅色,因為插入紅色元素只違背了第4條特性,那么我們只要根據(jù)這個特性來平衡就容易多了。

          根據(jù)不同的情況有以下幾種處理方式:


          1. 插入的元素如果是根節(jié)點,則直接涂成黑色即可,不用平衡;

          2. 插入的元素的父節(jié)點如果為黑色,不需要平衡;

          3. 插入的元素的父節(jié)點如果為紅色,則違背了特性4,需要平衡,平衡時又分成下面三種情況:

          (如果父節(jié)點是祖父節(jié)點的左節(jié)點)

          情況策略1)父節(jié)點為紅色,叔叔節(jié)點也為紅色(1)將父節(jié)點設(shè)為黑色;

          (2)將叔叔節(jié)點設(shè)為黑色;

          (3)將祖父節(jié)點設(shè)為紅色;

          (4)將祖父節(jié)點設(shè)為新的當(dāng)前節(jié)點,進(jìn)入下一次循環(huán)判斷;2)父節(jié)點為紅色,叔叔節(jié)點為黑色,且當(dāng)前節(jié)點是其父節(jié)點的右節(jié)點(1)將父節(jié)點作為新的當(dāng)前節(jié)點;

          (2)以新當(dāng)節(jié)點為支點進(jìn)行左旋,進(jìn)入情況3);3)父節(jié)點為紅色,叔叔節(jié)點為黑色,且當(dāng)前節(jié)點是其父節(jié)點的左節(jié)點(1)將父節(jié)點設(shè)為黑色;

          (2)將祖父節(jié)點設(shè)為紅色;

          (3)以祖父節(jié)點為支點進(jìn)行右旋,進(jìn)入下一次循環(huán)判斷;

          (如果父節(jié)點是祖父節(jié)點的右節(jié)點,則正好與上面反過來)

          情況策略1)父節(jié)點為紅色,叔叔節(jié)點也為紅色(1)將父節(jié)點設(shè)為黑色;

          (2)將叔叔節(jié)點設(shè)為黑色;

          (3)將祖父節(jié)點設(shè)為紅色;

          (4)將祖父節(jié)點設(shè)為新的當(dāng)前節(jié)點,進(jìn)入下一次循環(huán)判斷;2)父節(jié)點為紅色,叔叔節(jié)點為黑色,且當(dāng)前節(jié)點是其父節(jié)點的左節(jié)點(1)將父節(jié)點作為新的當(dāng)前節(jié)點;

          (2)以新當(dāng)節(jié)點為支點進(jìn)行右旋;3)父節(jié)點為紅色,叔叔節(jié)點為黑色,且當(dāng)前節(jié)點是其父節(jié)點的右節(jié)點(1)將父節(jié)點設(shè)為黑色;

          (2)將祖父節(jié)點設(shè)為紅色;

          (3)以祖父節(jié)點為支點進(jìn)行左旋,進(jìn)入下一次循環(huán)判斷;

          讓我們來看看TreeMap中的實現(xiàn):

          /**
           * 插入再平衡
           *(1)每個節(jié)點或者是黑色,或者是紅色。
           *(2)根節(jié)點是黑色。
           *(3)每個葉子節(jié)點(NIL)是黑色。(注意:這里葉子節(jié)點,是指為空(NIL或NULL)的葉子節(jié)點!)
           *(4)如果一個節(jié)點是紅色的,則它的子節(jié)點必須是黑色的。
           *(5)從一個節(jié)點到該節(jié)點的子孫節(jié)點的所有路徑上包含相同數(shù)目的黑節(jié)點。
           */
          private
          
          void
           fixAfterInsertion(
          Entry
           x) {
          
          // 插入的節(jié)點為紅節(jié)點,x為當(dāng)前節(jié)點
              x.color = RED;
          
          // 只有當(dāng)插入節(jié)點不是根節(jié)點且其父節(jié)點為紅色時才需要平衡(違背了特性4)
          
          while
           (x != 
          null
           && x != root && x.parent.color == RED) {
          
          if
           (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
          
          // a)如果父節(jié)點是祖父節(jié)點的左節(jié)點
          
          // y為叔叔節(jié)點
          
          Entry
           y = rightOf(parentOf(parentOf(x)));
          
          if
           (colorOf(y) == RED) {
          
          // 情況1)如果叔叔節(jié)點為紅色
          
          // (1)將父節(jié)點設(shè)為黑色
                          setColor(parentOf(x), BLACK);
          
          // (2)將叔叔節(jié)點設(shè)為黑色
                          setColor(y, BLACK);
          
          // (3)將祖父節(jié)點設(shè)為紅色
                          setColor(parentOf(parentOf(x)), RED);
          
          // (4)將祖父節(jié)點設(shè)為新的當(dāng)前節(jié)點
                          x = parentOf(parentOf(x));
                      } 
          else
           {
          
          // 如果叔叔節(jié)點為黑色
          
          // 情況2)如果當(dāng)前節(jié)點為其父節(jié)點的右節(jié)點
          
          if
           (x == rightOf(parentOf(x))) {
          
          // (1)將父節(jié)點設(shè)為當(dāng)前節(jié)點
                              x = parentOf(x);
          
          // (2)以新當(dāng)前節(jié)點左旋
                              rotateLeft(x);
                          }
          
          // 情況3)如果當(dāng)前節(jié)點為其父節(jié)點的左節(jié)點(如果是情況2)則左旋之后新當(dāng)前節(jié)點正好為其父節(jié)點的左節(jié)點了)
          
          // (1)將父節(jié)點設(shè)為黑色
                          setColor(parentOf(x), BLACK);
          
          // (2)將祖父節(jié)點設(shè)為紅色
                          setColor(parentOf(parentOf(x)), RED);
          
          // (3)以祖父節(jié)點為支點進(jìn)行右旋
                          rotateRight(parentOf(parentOf(x)));
                      }
                  } 
          else
           {
          
          // b)如果父節(jié)點是祖父節(jié)點的右節(jié)點
          
          // y是叔叔節(jié)點
          
          Entry
           y = leftOf(parentOf(parentOf(x)));
          
          if
           (colorOf(y) == RED) {
          
          // 情況1)如果叔叔節(jié)點為紅色
          
          // (1)將父節(jié)點設(shè)為黑色
                          setColor(parentOf(x), BLACK);
          
          // (2)將叔叔節(jié)點設(shè)為黑色
                          setColor(y, BLACK);
          
          // (3)將祖父節(jié)點設(shè)為紅色
                          setColor(parentOf(parentOf(x)), RED);
          
          // (4)將祖父節(jié)點設(shè)為新的當(dāng)前節(jié)點
                          x = parentOf(parentOf(x));
                      } 
          else
           {
          
          // 如果叔叔節(jié)點為黑色
          
          // 情況2)如果當(dāng)前節(jié)點為其父節(jié)點的左節(jié)點
          
          if
           (x == leftOf(parentOf(x))) {
          
          // (1)將父節(jié)點設(shè)為當(dāng)前節(jié)點
                              x = parentOf(x);
          
          // (2)以新當(dāng)前節(jié)點右旋
                              rotateRight(x);
                          }
          
          // 情況3)如果當(dāng)前節(jié)點為其父節(jié)點的右節(jié)點(如果是情況2)則右旋之后新當(dāng)前節(jié)點正好為其父節(jié)點的右節(jié)點了)
          
          // (1)將父節(jié)點設(shè)為黑色
                          setColor(parentOf(x), BLACK);
          
          // (2)將祖父節(jié)點設(shè)為紅色
                          setColor(parentOf(parentOf(x)), RED);
          
          // (3)以祖父節(jié)點為支點進(jìn)行左旋
                          rotateLeft(parentOf(parentOf(x)));
                      }
                  }
              }
          
          // 平衡完成后將根節(jié)點設(shè)為黑色
              root.color = BLACK;
          }
          

          插入元素舉例

          我們依次向紅黑樹中插入 4、2、3 三個元素,來一起看看整個紅黑樹平衡的過程。

          三個元素都插入完成后,符合父節(jié)點是祖父節(jié)點的左節(jié)點,叔叔節(jié)點為黑色,且當(dāng)前節(jié)點是其父節(jié)點的右節(jié)點,即情況2)。

          mZro3W12VB.png

          情況2)需要做以下兩步處理:

          (1)將父節(jié)點作為新的當(dāng)前節(jié)點;

          (2)以新當(dāng)節(jié)點為支點進(jìn)行左旋,進(jìn)入情況3);

          VIVdNkl8VQ.png

          情況3)需要做以下三步處理:

          (1)將父節(jié)點設(shè)為黑色;

          (2)將祖父節(jié)點設(shè)為紅色;

          (3)以祖父節(jié)點為支點進(jìn)行右旋,進(jìn)入下一次循環(huán)判斷;

          eck2UHR62Z.png

          下一次循環(huán)不符合父節(jié)點為紅色了,退出循環(huán),插入再平衡完成。

          未完待續(xù),下一節(jié)我們一起探討紅黑樹刪除元素的操作。


          瀏覽 73
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  色五月丁香亚洲 | 中国女人真人一级毛片 | 欧美精品性视频 | 亚洲最大视频在线观看 | 青娱乐亚洲精品视频在线观看 |