最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java數(shù)據(jù)結(jié)構(gòu)之紅黑樹(shù)的真正理解

 更新時(shí)間:2017年11月02日 08:51:53   作者:何錦彬  
這篇文章主要為大家詳細(xì)介紹了Java數(shù)據(jù)結(jié)構(gòu)之紅黑樹(shù)的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

真正的幫助大家理解紅黑樹(shù):

一、紅黑樹(shù)所處數(shù)據(jù)結(jié)構(gòu)的位置:

在JDK源碼中, 有treeMap和JDK8的HashMap都用到了紅黑樹(shù)去存儲(chǔ)

紅黑樹(shù)可以看成B樹(shù)的一種:

二叉樹(shù)看,紅黑樹(shù)是一顆相對(duì)平衡的二叉樹(shù)

二叉樹(shù)-->搜索二叉樹(shù)-->平衡搜索二叉樹(shù)--> 紅黑樹(shù)

N階樹(shù)看,紅黑樹(shù)就是一顆 2-3-4樹(shù)

N階樹(shù)-->B(B-)樹(shù)

 故我提取出了紅黑樹(shù)部分的源碼,去說(shuō)明紅黑樹(shù)的理解

看之前,理解紅黑樹(shù)的幾個(gè)特性,后面的操作都是為了讓樹(shù)符合紅黑樹(shù)的這幾個(gè)特性,從而滿足對(duì)查找效率的O(logn)

二、紅黑樹(shù)特性,以及保持的手段

1.根和葉子節(jié)點(diǎn)都是黑色的

2.不能有有連續(xù)兩個(gè)紅色的節(jié)點(diǎn)

3.從任一節(jié)點(diǎn)到它所能到達(dá)得葉子節(jié)點(diǎn)的所有簡(jiǎn)單路徑都包含相同數(shù)目的黑色節(jié)點(diǎn) 

這幾個(gè)特效,個(gè)人理解就是規(guī)定了紅黑樹(shù)是一顆2-3-4的B樹(shù)了,從而滿足了O(logn)查找效率 

保持特性的手段,通過(guò)下面這些手段,讓紅黑樹(shù)滿足紅黑樹(shù)的特性,如果要嘗試?yán)斫?,可以?-3-4樹(shù)的向上增長(zhǎng),后面有詳細(xì)介紹 

當(dāng)然,這些改變也都是在O(logn)內(nèi)完成的,主要改變方式有

1.改變顏色

2.左旋

3.右旋 

三、從JDK源碼來(lái)理解

主要看我的注釋,邏輯的理解

先看TreeMap

//對(duì)treeMap的紅黑樹(shù)理解注解. 2017.02.16 by 何錦彬 JDK,1.7.51<br> <br>/** From CLR */
 private void fixAfterInsertion(Entry<K, V> x) {
 
   
   
  //新加入紅黑樹(shù)的默認(rèn)節(jié)點(diǎn)就是紅色
  x.color = RED;
  /**
   * 1. 如為根節(jié)點(diǎn)直接跳出
   */
  while (x != null && x != root && x.parent.color == RED) {
    
   if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
     
    //如果X的父節(jié)點(diǎn)(P)是其父節(jié)點(diǎn)的父節(jié)點(diǎn)(G)的左節(jié)點(diǎn)
    //即 下面這種情況
    /**
     *       G
     *    P(RED)    U
     */    
    //獲取其叔(U)節(jié)點(diǎn)
    Entry<K, V> y = rightOf(parentOf(parentOf(x)));
    if (colorOf(y) == RED) {
     // 這種情況,對(duì)應(yīng)下面 圖:情況一
     /**
      *       G
      *    P(RED)    U(RED)
      *   X
      */
     //如果叔節(jié)點(diǎn)是紅色的(父節(jié)點(diǎn)有判斷是紅色). 即是雙紅色,比較好辦,通過(guò)改變顏色就行. 把P和U都設(shè)置成黑色然后,X加到P節(jié)點(diǎn)。 G節(jié)點(diǎn)當(dāng)作新加入節(jié)點(diǎn)繼續(xù)迭代
     setColor(parentOf(x), BLACK);
     setColor(y, BLACK);
     setColor(parentOf(parentOf(x)), RED);
     x = parentOf(parentOf(x));
    } else {
     //處理紅父,黑叔的情況
     if (x == rightOf(parentOf(x))) {
      // 這種情況,對(duì)應(yīng)下面 圖:情況二
      /**
       *       G
       *   P(RED)      U(BLACK)
       *      X
       */
      //如果X是右邊節(jié)點(diǎn)
      x = parentOf(x);
      // 進(jìn)行左旋
      rotateLeft(x);
     }
     //左旋后,是這種情況了,對(duì)應(yīng)下面 圖:情況三
     /**
      *       G
      *   P(RED)      U(BLACK)
      *  X
      */
      
     // 到這,X只能是左節(jié)點(diǎn)了,而且P是紅色,U是黑色的情況
     //把P改成黑色,G改成紅色,以G為節(jié)點(diǎn)進(jìn)行右旋
     setColor(parentOf(x), BLACK);
     setColor(parentOf(parentOf(x)), RED);
     rotateRight(parentOf(parentOf(x)));
    }
   } else {
    //父節(jié)點(diǎn)在右邊的
    /**
     *       G
     *     U    P(RED)
     */    
    //獲取U
    Entry<K, V> y = leftOf(parentOf(parentOf(x)));
     
    if (colorOf(y) == RED) {
     //紅父紅叔的情況
     /**
      *       G
      *    U(RED)    P(RED)
      */ 
     setColor(parentOf(x), BLACK);
     setColor(y, BLACK);
     setColor(parentOf(parentOf(x)), RED);
     //把G當(dāng)作新插入的節(jié)點(diǎn)繼續(xù)進(jìn)行迭代
     x = parentOf(parentOf(x));
    } else {
     //紅父黑叔,并且是右父的情況
     /**
      *       G
      *    U(RED)    P(RED)
      */ 
     if (x == leftOf(parentOf(x))) {
     //如果插入的X是左節(jié)點(diǎn)
      /**
      *       G
      *   U(BLACK)      P(RED)
      *          X    
      */
      x = parentOf(x);
      //以P為節(jié)點(diǎn)進(jìn)行右旋
      rotateRight(x);
     }
     //右旋后
     /**
      *       G
      *   U(BLACK)      P(RED)
      *              X
      */
     setColor(parentOf(x), BLACK);
     setColor(parentOf(parentOf(x)), RED);
     //以G為節(jié)點(diǎn)進(jìn)行左旋
     rotateLeft(parentOf(parentOf(x)));
    }
   }
  }
  //紅黑樹(shù)的根節(jié)點(diǎn)始終是黑色
  root.color = BLACK;
 }

再看看HashMap的實(shí)現(xiàn),在HashMap中,在JDK8后開(kāi)始用紅黑樹(shù)代替鏈表,查找由O(n) 變成了 O(Logn)

源碼分析如下:

for (int binCount = 0; ; ++binCount) {
     if ((e = p.next) == null) {
      p.next = newNode(hash, key, value, null);
      //JDK8 的hashmap,鏈表到了8就需要變成顆紅黑樹(shù)了
      if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
       treeifyBin(tab, hash);
      break;
     }


紅黑樹(shù)的維護(hù)代碼部分如下:

//hashmap的紅黑樹(shù)平衡
   static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
               TreeNode<K,V> x) {
     x.red = true;
     //死循環(huán)加變量定義,總感覺(jué)JAVA很少這樣寫代碼 哈
     for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
      //xp X父節(jié)點(diǎn), XPP X的祖父節(jié)點(diǎn), XPPL 祖父左節(jié)點(diǎn) XXPR 祖父右節(jié)點(diǎn) 
      if ((xp = x.parent) == null) {
       x.red = false;
       return x;
      }
      // 如果父節(jié)點(diǎn)是黑色, 或者XP父節(jié)點(diǎn)是空,直接返回
      else if (!xp.red || (xpp = xp.parent) == null)
       return root;
      
      // 下面的代碼就和上面的很treeMap像了,
      
      if (xp == (xppl = xpp.left)) {
        // 第一種情況, 賦值xppl
       //父節(jié)點(diǎn)是左節(jié)點(diǎn)的情況,下面這種
       /**
        *       G
        *    P(RED)    U
        */    
       if ((xppr = xpp.right) != null && xppr.red) {
        //如果紅叔的情況
         // 這種情況,對(duì)應(yīng)下面 圖:情況一
        /**
         *       G
         *    P(RED)    U(RED)
         *   X
         */
         //改變其顏色,
        xppr.red = false;
        xp.red = false;
        xpp.red = true;
        x = xpp;
       }
       else {
         // 黑叔的情況
        // 這種情況
        /**
         *       G
         *    P(RED)    U(BLACK)
         */
        if (x == xp.right) {
         //如果插入節(jié)點(diǎn)在右邊 這種
          // 這種情況,對(duì)應(yīng)下面 圖:情況二
         /**
          *       G
          *   P(RED)      U(BLACK)
          *      X
          */
         //需要進(jìn)行左旋
         root = rotateLeft(root, x = xp);
         xpp = (xp = x.parent) == null ? null : xp.parent;
        }
        //左旋后情況都是這種了,對(duì)應(yīng)下面 圖:情況三
        /**
         *       G
         *   P(RED)      U(BLACK)
         *  X
         */
        // 到這,X只能是左節(jié)點(diǎn)了,而且P是紅色,U是黑色的情況
        if (xp != null) {
        //把P改成黑色,G改成紅色, 以G為節(jié)點(diǎn)進(jìn)行右旋
         xp.red = false;
         if (xpp != null) {
          xpp.red = true;
          root = rotateRight(root, xpp);
         }
        }
       }
      }
      else {
        //父節(jié)點(diǎn)在右邊的
       /**
        *       G
        *     U    P(RED)
        */    
       //獲取U
       if (xppl != null && xppl.red) {
        //紅父紅叔的情況
         /**
         *       G
         *     U(RED)    P(RED)
         */    
        xppl.red = false;
        xp.red = false;
        xpp.red = true;
        x = xpp;
       }
       else {
        
        if (x == xp.left) {
         //如果插入的X是右節(jié)點(diǎn)
         /**
          *       G
          *   U(BLACK)      P(RED)
          *          X    
          */
         root = rotateRight(root, x = xp);
         xpp = (xp = x.parent) == null ? null : xp.parent;
        }
        //右旋后
        /**
         *       G
         *   U(BLACK)      P(RED)
         *              X
         */
        if (xp != null) {
         //把P改成黑色,G改成紅色,
         xp.red = false;
         if (xpp != null) {
          xpp.red = true;
          //以G節(jié)點(diǎn)左旋
          root = rotateLeft(root, xpp);
         }
        }
       }
      }
 }

情況圖如下

情況1

情況2

情況3

JDK源碼處理紅黑樹(shù)的流程圖

可見(jiàn),其實(shí)處理邏輯實(shí)現(xiàn)都一樣的

三、個(gè)人對(duì)紅黑樹(shù)理解的方法

1. 如何理解紅黑樹(shù)的O(lgN)的特性?

從2-3-4樹(shù)去理解

紅黑樹(shù),其實(shí)是一顆 2-3-4的B樹(shù),B樹(shù)都是向上增長(zhǎng)的,如果不理解向上增長(zhǎng)可以先看看2-3樹(shù),這樣理解就能知道為什么能O(logn)的查找了

2.如何理解紅黑樹(shù)的紅黑節(jié)點(diǎn)意義?

可以把紅色節(jié)點(diǎn)看成是連接父節(jié)點(diǎn)的組成的一個(gè)大節(jié)點(diǎn)(2個(gè)或3個(gè)或4個(gè)節(jié)點(diǎn)組成的一個(gè)key),如下:

(此圖轉(zhuǎn)自網(wǎng)上)

紅色的就是和父節(jié)點(diǎn)組成了大節(jié)點(diǎn),

比如

節(jié)點(diǎn)7和6,6是紅色節(jié)點(diǎn)組成,故和它父節(jié)點(diǎn)7組成了一個(gè)大節(jié)點(diǎn),即 2-3-4樹(shù)的 6, 7節(jié)點(diǎn)

又如

節(jié)點(diǎn) 9和10和11,9和10為紅色節(jié)點(diǎn),故和10組成了一個(gè)2-3-4的3階節(jié)點(diǎn), 9,10,11(注意順序有的關(guān)性)

3.B樹(shù)是如何保持O(lgn)的復(fù)雜度的呢?

B+樹(shù)都是從底布開(kāi)始往上生長(zhǎng),自動(dòng)平衡,如 2-3-4樹(shù),當(dāng)節(jié)點(diǎn)達(dá)到了3個(gè)時(shí)晉升到上個(gè)節(jié)點(diǎn),所以不會(huì)產(chǎn)生單獨(dú)生長(zhǎng)一邊的情況,形成平衡。

留個(gè)問(wèn)題

4.數(shù)據(jù)庫(kù)里的索引為什么不用紅黑樹(shù)而是用B+樹(shù)(Mysql)呢?

后續(xù)解答

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java中線程死亡的幾種情況實(shí)例分析

    Java中線程死亡的幾種情況實(shí)例分析

    線程是進(jìn)程中的一個(gè)實(shí)體,是被系統(tǒng)獨(dú)立調(diào)度和分派的基本單位,線程自己不擁有系統(tǒng)資源,只擁有一點(diǎn)在運(yùn)行中必不可少的資源,但它可與同屬一個(gè)進(jìn)程的其它線程共享進(jìn)程所擁有的全部資源。下面這篇文章主要給大家介紹了Java線程死亡的幾種情況,需要的朋友可以參考下。
    2017-01-01
  • 基于HTML5+js+Java實(shí)現(xiàn)單文件文件上傳到服務(wù)器功能

    基于HTML5+js+Java實(shí)現(xiàn)單文件文件上傳到服務(wù)器功能

    應(yīng)公司要求,在HTML5頁(yè)面上實(shí)現(xiàn)上傳文件到服務(wù)器功能,對(duì)于我這樣的菜鳥(niǎo),真是把我難住了,最后還是請(qǐng)教大神搞定的,下面小編把例子分享到腳本之家平臺(tái),供大家參考
    2017-08-08
  • Java中實(shí)現(xiàn)分布式定時(shí)任務(wù)的方法

    Java中實(shí)現(xiàn)分布式定時(shí)任務(wù)的方法

    這篇文章主要介紹了Java中實(shí)現(xiàn)分布式定時(shí)任務(wù),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • 怎樣通過(guò)反射獲取非靜態(tài)內(nèi)部類實(shí)例

    怎樣通過(guò)反射獲取非靜態(tài)內(nèi)部類實(shí)例

    這篇文章主要介紹了怎樣通過(guò)反射獲取非靜態(tài)內(nèi)部類實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • Java 如何實(shí)現(xiàn)一個(gè)http服務(wù)器

    Java 如何實(shí)現(xiàn)一個(gè)http服務(wù)器

    這篇文章主要介紹了Java 如何實(shí)現(xiàn)一個(gè)http服務(wù)器,幫助大家更好的理解和學(xué)習(xí)Java,感興趣的朋友可以了解下
    2020-11-11
  • Spring Boot優(yōu)雅使用RocketMQ的方法實(shí)例

    Spring Boot優(yōu)雅使用RocketMQ的方法實(shí)例

    這篇文章主要給大家介紹了關(guān)于Spring Boot優(yōu)雅使用RocketMQ的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Spring Boot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • SpringCloud Alibaba Seata (收藏版)

    SpringCloud Alibaba Seata (收藏版)

    Seata是一款開(kāi)源的分布式事務(wù)解決方案,致力于在微服務(wù)架構(gòu)在提供高性能和簡(jiǎn)單一樣的分布式事務(wù)服務(wù)。這篇文章主要介紹了SpringCloud Alibaba Seata 的相關(guān)知識(shí),需要的朋友可以參考下
    2020-10-10
  • Java實(shí)現(xiàn)異步執(zhí)行的8種方式總結(jié)

    Java實(shí)現(xiàn)異步執(zhí)行的8種方式總結(jié)

    這篇文章主要給大家介紹了關(guān)于Java實(shí)現(xiàn)異步執(zhí)行的8種方式,異步編程不會(huì)阻塞程序的執(zhí)行,它將耗時(shí)的操作提交給后臺(tái)線程或其他執(zhí)行環(huán)境,并立即返回,使得程序可以繼續(xù)執(zhí)行其他任務(wù),需要的朋友可以參考下
    2023-09-09
  • 老生常談Scanner的基本用法

    老生常談Scanner的基本用法

    下面小編就為大家?guī)?lái)一篇老生常談Scanner的基本用法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-07-07
  • MyBatis主鍵生成策略中useGeneratedKeys和<selectKey>的區(qū)別

    MyBatis主鍵生成策略中useGeneratedKeys和<selectKey>的區(qū)別

    本文主要介紹了MyBatis主鍵生成策略中useGeneratedKeys和<selectKey>的區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-01-01

最新評(píng)論

宜阳县| 九江市| 青浦区| 扎赉特旗| 固原市| 永嘉县| 广宗县| 四会市| 红河县| 葵青区| 隆德县| 彭州市| 正宁县| 华亭县| 香格里拉县| 南召县| 新乡县| 江阴市| 华宁县| 米易县| 虹口区| 封丘县| 右玉县| 子洲县| 会东县| 兖州市| 安图县| 隆林| 延庆县| 礼泉县| 醴陵市| 连云港市| 舒城县| 墨竹工卡县| 凯里市| 无为县| 横山县| 临邑县| 开平市| 浮山县| 新安县|