數據結構之樹——二叉查找樹、2-3樹、2-3-4樹、紅黑樹、B樹、B+樹及其關係

前言  

對於大量的輸入數據,鏈表的線性訪問時間太慢,不宜使用。由此,其他的更高效的數據結構被提出,二叉查找樹是其中重要的一個。二叉查找樹是兩種庫集合類TreeSet和TreeMap實現的基礎,應用很廣泛。在計算機科學中樹(tree)是非常有用的抽象概念。

樹的定義

定義樹的一種自然的方式是遞歸的方式。一棵樹是一些節點的集合,這個集合可以是空集;若不是空集,則樹由稱作根(root)的節點r與0個或多個非空的(子)樹組成,每一棵子樹的根節點都被來自根r的一條有向的邊所聯結。這些子樹的根節點是根r的子節點,根r是這些子樹根節點的父節點。從而,樹的每一個節點都必有一個父節點,但不一定有子節點,沒有子節點的節點被稱爲葉節點。

樹的實現

實現樹的一種方法可以是在每一個節點除數據外還要有一些鏈,使得該節點的每一個子節點都有一個鏈指向它,然而由於每個節點的子節點個數可以有很大的變化並且事先不知道,因此在數據結構中建立到各子節點直接的鏈接是不行的,因爲這樣會浪費太多的空間(對於結構簡單的二叉樹,可以直接使用第一種實現)。實際上解決方法很簡單:將每個節點所有的子節點都放在樹節點的鏈表中,形如以下的聲明:

class TreeNode{
   Object element;
   TreeNode firstChild;  //首個子節點
   TreeNode nextSibling; //下一個兄弟節點
}

樹的遍歷

先序遍歷、中序遍歷、後序遍歷,參考:https://blog.csdn.net/qq_33243189/article/details/80222629



二叉查找樹

二叉樹是一棵樹,其中每個節點都不能有多於兩個的子節點。二叉查找樹是二叉樹的特殊情況,使得二叉樹成爲二叉查找樹的性質是,對於樹中的每個節點X,它的左子樹中所有項的值都要小於X中的項,而它的右子樹中所有項的值都要大於X中的項。因此,二叉查找樹要求節點能夠排序,在Java中要寫出一個可以用於存儲在二叉查找樹中的類,那麼該類必須實現Comparable接口。

二叉查找樹的查找、插入與刪除

重點和難點在於二叉查找樹當待刪除的節點有兩個子節點的情況下的刪除操作。

簡而言之,當待刪除的節點有兩個子節點的時候,可以用該節點的右子樹中最小的節點——即從右子樹的根節點一直向左遍歷到的最後一個節點——來替代被刪除的節點。若是右子樹中最小的節點還有一個右節點,則用該右節點替換右子樹中的最小節點即可。

例如上面的二叉查找樹,如果要刪除13,則在其右子樹中找到最靠左的節點12,用該節點代替13。對於12的右節點14,則可用14來替代原來12的位置,最後得到的二叉查找樹爲:

詳細內容參考:https://blog.csdn.net/qq_33240946/article/details/82421882



對於二叉查找誰、2-3樹、2-3-4樹、紅黑樹、B樹、B+樹的關係,可以用一個圖概括:

  • 二叉查找樹(有序二叉樹):最多有兩個子節點,左子樹所有節點值小於根節點值,右子樹所有節點值大於根節點值。每個節點Node包含兩個分別指向左右節點的Node、一個用於排序的Key、一個該節點包含的值Value以及一個記錄該節點及所有子節點個數的Number。
  • http://www.cnblogs.com/yangecnu/p/Introduce-2-3-Search-Tree.html
    二三樹是一種平衡的查找樹,但不是二叉樹。二三樹的每個節點保存1個或2個Key值。對於普通的2-Node,保存1個用於排序的Key和Value值以及左右兩個子節點,左右子節點分別滿足Key值小於和大於根節點Key值;對於3-Node,保存兩個Key與對應Value值,以及左中右三個子節點,這三個子節點分別滿足Key值小於根節點最小的Key值、介於兩個Key之間、大於根節點最大的Key值;二三樹通過不同情況下選用不同的插入方式來保持查找樹是平衡的
  • https://blog.csdn.net/qq_26222859/article/details/80631121    http://lauandy.cc/2017/08/03/btree-and-bplustree/
    B樹是平衡多叉樹,可以看做是對二三樹的一種擴展,即允許每個節點有最多M個子節點,其中M爲B樹的階。每個節點的多個key按升序排列,且有 節點所含key值的個數 = 節點的子樹的個數 – 1,就意味着某節點的每個子樹所在的位置是由該節點的key值的分佈決定的。B樹的另外一個特性就是所有的葉子節點都處於同一層,也就是說從根節點到任意節點的深度都相等(平衡)
  • B+樹是B樹的一種變形,二者的差異在於,非葉子節點的節點(就是中間節點)的子樹的個數 = 該節點的key的個數,這是因爲B+樹中的中間節點的key並不用於保存數據,而只用來索引,而葉子節點中包含了全部的key以及value;所有的中間節點的key都同時存在於子節點,且在子節點的key中是最大或者最小的;所有的子節點都按照升序以指針連接在一起。由於B+樹的中間節點不再存儲value,那麼同樣大小的磁盤頁可以容納更多的節點元素,因此數據量相同的B樹與B+樹相比,後者更加“矮胖”,從而可以減少磁盤I/O。B+樹因爲每次都要查找到葉子節點,因此查找性能穩定。範圍查詢時只需在葉子節點順序遍歷,更簡單。
  • https://www.cnblogs.com/yangecnu/p/Introduce-Red-Black-Tree.html 限定左旋的紅黑樹:是二三樹的變體
    HashMap-紅黑樹
    在線生成紅黑樹
    紅黑樹:Java HashMap的紅黑樹是普通的紅黑樹,既可以左旋也可以右旋的,與上面blog裏的紅節點只能作爲左子樹節點的規則是不一樣的。紅黑樹從根到葉子節點的最長路徑不會超過最短路徑的二倍。紅黑樹是黑色平衡的,即從根節點到所有葉子節點的路徑,經過的黑色的節點數是相等的。  本質上來說,紅黑樹這個數據結構的基本思想源自於階數爲4的B樹(也就是2-3-4樹)。一個這樣的紅黑樹,本質上是一個這樣的4階B樹:
    • 因此對於紅黑樹的插入等操作,可以類比4階B樹的相關操作。
      1. 要將一個節點A插入,首先要將該節點的key值與樹中的節點Key值相比較,最後找到一個正確的null節點位置,然後替換此null節點。此時節點A的顏色爲紅色。
      2. if A節點父節點爲黑色,那麼插入過程結束,因爲紅色的節點不會影響紅黑樹的平衡
          if A節點父節點爲紅色,那麼此時要看A節點的父節點的兄弟節點,也就是A節點的叔叔節點:
              if  A節點沒有叔叔節點,則進行紅黑樹的旋轉操作(左旋或右旋),最後的情況是,A節點的爺爺節點的位置由A節點的父節點替換了,A節點原來的爺爺節點在旋轉後成爲了A節點的父節點的子節點,也就是A節點的爺爺節點成爲了A節點的兄弟節點。注意,旋轉的過程中,不光A節點的父節點與爺爺節點的位置要變換,兩個節點的顏色也要變:
      插入14 →  a    
             if  A節點有叔叔節點(可知A節點的叔叔節點顏色應該與A節點的父節點相同,即爲紅色)  則將A節點的父節點和叔叔節點顏色全部置爲黑色,而將A節點的爺爺節點的顏色置爲紅色,以保證整個紅黑樹仍然是黑色平衡的。   此時A節點的爺爺節點爲紅色,可能會導致不再滿足紅黑樹的約束,因此要對變成紅色的爺爺節點進行與插入A節點時相同的操作,來維持整個紅黑樹的平衡,這個過程可能是一個遞歸的過程
       插入9 →  
    • 關於紅黑樹左旋和右旋:左旋右上位,右旋左上位。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章