1. 程式人生 > >AVL樹、紅黑樹以及B樹介紹

AVL樹、紅黑樹以及B樹介紹

數值 stl linux 場景 基於 -a pre 搜索 alt

簡介

首先,說一下在數據結構中為什麽要引入樹這種結構,在我們上篇文章中介紹的數組與鏈表中,可以發現,數組適合查詢這種靜態操作(O(1)),不合適刪除與插入這種動態操作(O(n)),而鏈表則是適合刪除與插入,而查詢效率則就比較慢了,本文要分享學習的樹就是為了平衡這種靜態操作與動態操作的差距。

一、二叉查找樹

簡介

滿足下面條件就是二叉查找樹

  • 任意節點左子樹不為空,則左子樹的值均小於根節點的值.
  • 任意節點右子樹不為空,則右子樹的值均大於於根節點的值.
  • 任意節點的左右子樹也分別是二叉查找樹.
  • 沒有鍵值相等的節點.

技術分享圖片

技術分享圖片

二叉查找樹在最壞的情況下就是會退化成一個有n個節點的線性鏈

為了防止這些壞情況發生,在二叉查找樹的基礎上,又做了一些限制,這也就出現了AVL樹(平衡二叉查找樹),紅黑樹

二、AVL樹(平衡二叉樹)

說一下時間復雜度的計算
由2^x=n 得到x=log2n。 所以這個循環的時間復雜度為O(log2n)。

簡介

AVL樹是帶有平衡條件的二叉查找樹,一般是用平衡因子差值判斷是否平衡並通過旋轉來實現平衡,左右子樹樹高不超過1,和紅黑樹相比,它是嚴格的平衡二叉樹,平衡條件必須滿足(所有節點的左右子樹高度差不超過1).不管我們是執行插入還是刪除操作,只要不滿足上面的條件,就要通過旋轉來保持平衡,而旋轉是非常耗時的,由此我們可以知道AVL樹適合用於插入刪除次數比較少,但查找多的情況。

技術分享圖片

局限性

由於維護這種高度平衡所付出的代價比從中獲得的效率收益還大,故而實際的應用不多,更多的地方是用追求局部而不是非常嚴格整體平衡的紅黑樹.當然,如果應用場景中對插入刪除不頻繁,只是對查找要求較高,那麽AVL還是較優於紅黑樹.

查找效率O(log2 n)(2為底)

應用

Windows進程地址空間管理?

三、紅黑樹

技術分享圖片

簡介

一種二叉查找樹,但在每個節點增加一個存儲位表示節點的顏色,可以是red或black. 通過對任何一條從根到葉子的路徑上各個節點著色的方式的限制,紅黑樹確保沒有一條路徑會比其它路徑長出兩倍.它是一種弱平衡二叉樹(由於是若平衡,可以推出,相同的節點情況下,AVL樹的高度低於紅黑樹),相對於要求嚴格的AVL樹來說,它的旋轉次數變少,所以對於搜索,插入,刪除操作多的情況下,我們就用紅黑樹.

它雖然是復雜的,但它的最壞情況運行時間也是非常良好的,並且在實踐中是高效的: 它可以在O(log2 n)(2為底)時間內做查找,插入和刪除

,這裏的n 是樹中元素的數目。

性質

  • 每個節點非紅即黑.
  • 根節點是黑的。
  • 每個葉節點(葉節點即樹尾端NUL指針或NULL節點)都是黑的.
  • 如果一個節點是紅的,那麽它的兩兒子都是黑的.
  • 對於任意節點而言,其到葉子點樹NIL指針的每條路徑都包含相同數目的黑節點.

    應用

  • 廣泛用於C++的STL中,map和set都是用紅黑樹實現的.
  • 著名的linux進程調度Completely Fair Scheduler,用紅黑樹管理進程控制塊,進程的虛擬內存區域都存儲在一顆紅黑樹上,每個虛擬地址區域都對應紅黑樹的一個節點,左指針指向相鄰的地址虛擬存儲區域,右指針指向相鄰的高地址虛擬地址空間.
  • IO多路復用epoll的實現采用紅黑樹組織管理sockfd,以支持快速的增刪改查.
  • ngnix中,用紅黑樹管理timer,因為紅黑樹是有序的,可以很快的得到距離當前最小的定時器.
  • java中TreeMap的實現.

    四、紅黑樹 VS AVL樹

紅黑樹是通過復雜的節點插入、節點顏色變換後實現的:這些功能經典的AVL樹也能實現,為什麽要提出紅黑樹?

首先紅黑樹是不符合AVL樹的平衡條件的,即每個節點的左子樹和右子樹的高度最多差1的二叉查找樹。但是提出了為節點增加顏色,紅黑是用非嚴格的平衡來換取增刪節點時候旋轉次數的降低,任何不平衡都會在三次旋轉之內解決,而AVL是嚴格平衡樹,因此在增加或者刪除節點的時候,根據不同情況,旋轉的次數比紅黑樹要多。所以紅黑樹的插入效率更高!!!

對於插入刪除操作很少,又很頻繁地查詢的,可以用紅黑樹實現

五、B 樹(多叉樹)

B-Tree介紹

B-樹的搜索,從根結點開始,對結點內的關鍵字(有序)序列進行二分查找,如果命中則結束,否則進入查詢關鍵字所屬範圍的兒子結點;重復,直到所對應的兒子指針為空,或已經是葉子結點;

B-Tree是一種多路搜索樹(並不是二叉的):

1.定義任意非葉子結點最多只有M個兒子;且M>2;
   2.根結點的兒子數為[2, M];
   3.除根結點以外的非葉子結點的兒子數為[M/2, M];
   4.每個結點存放至少M/2-1(取上整)和至多M-1個關鍵字;(至少2個關鍵字)
   5.非葉子結點的關鍵字個數=指向兒子的指針個數-1;
   6.非葉子結點的關鍵字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];
   7.非葉子結點的指針:P[1], P[2], …, P[M];其中P[1]指向關鍵字小於K[1]的子樹,P[M]指向關鍵字大於K[M-1]的子樹,其它P[i]指向關鍵字屬於(K[i-1], K[i])的子樹;
   8.所有葉子結點位於同一層;
  

技術分享圖片

B-樹的特性:

   1.關鍵字集合分布在整顆樹中;
   2.任何一個關鍵字出現且只出現在一個結點中;
   3.搜索有可能在非葉子結點結束;
   4.其搜索性能等價於在關鍵字全集內做一次二分查找;
   5.自動層次控制;
  

B+Tree介紹

B+樹是B-樹的變體,也是一種多路搜索樹:

   1.其定義基本與B-樹同,除了:
   2.非葉子結點的子樹指針與關鍵字個數相同;
   3.非葉子結點的子樹指針P[i],指向關鍵字值屬於[K[i], K[i+1])的子樹(B-樹是開區間);
   5.為所有葉子結點增加一個鏈指針;
   6.所有關鍵字都在葉子結點出現;
   

技術分享圖片

B+的搜索與B-樹也基本相同,區別是

B+樹只有達到葉子結點才命中(B-樹可以在非葉子結點命中),其性能也等價於在關鍵字全集做一次二分查找;

B+的特性:

   1.所有關鍵字都出現在葉子結點的鏈表中(稠密索引),且鏈表中的關鍵字恰好是有序的;
   2.不可能在非葉子結點命中;
   3.非葉子結點相當於是葉子結點的索引(稀疏索引),葉子結點相當於是存儲(關鍵字)數據的數據層;
   4.更適合文件索引系統;
   

B樹與B+樹區別

這都是由於B+樹和B具有這不同的存儲結構所造成的區別,以一個m階樹為例。

關鍵字的數量不同;B+樹中分支結點有m個關鍵字,其葉子結點也有m個,其關鍵字只是起到了一個索引的作用,但是B樹雖然也有m個子結點,但是其只擁有m-1個關鍵字。

存儲的位置不同;B+樹中的數據都存儲在葉子結點上,也就是其所有葉子結點的數據組合起來就是完整的數據,但是B樹的數據存儲在每一個結點中,並不僅僅存儲在葉子結點上。

分支結點的構造不同;B+樹的分支結點僅僅存儲著關鍵字信息和兒子的指針(這裏的指針指的是磁盤塊的偏移量),也就是說內部結點僅僅包含著索引信息。

查詢不同;B樹在找到具體的數值以後,則結束,而B+樹則需要通過索引找到葉子結點中的數據才結束,也就是說B+樹的搜索過程中走了一條從根結點到葉子結點的路徑

為什麽說B+比B樹更適合實際應用中操作系統的文件索引和數據庫索引?

1) B+的磁盤讀寫代價更低

B+的內部結點並沒有指向關鍵字具體信息的指針。因此其內部結點相對B樹更小。如果把所有同一內部結點的關鍵字存放在同一盤塊中,那麽盤塊所能容納的關鍵字數量也越多。一次性讀入內存中的需要查找的關鍵字也就越多。相對來說IO讀寫次數也就降低了。

2) B+-tree的查詢效率更加穩定

由於非終結點並不是最終指向文件內容的結點,而只是葉子結點中關鍵字的索引。所以任何關鍵字的查找必須走一條從根結點到葉子結點的路。所有關鍵字查詢的路徑長度相同,導致每一個數據的查詢效率相當。

數據庫索引采用B+樹的主要原因是 B樹在提高了磁盤IO性能的同時並沒有解決元素遍歷的效率低下的問題。正是為了解決這個問題,B+樹應運而生。B+樹只要遍歷葉子節點就可以實現整棵樹的遍歷。而且在數據庫中基於範圍的查詢是非常頻繁的,而B樹不支持這樣的操作(或者說效率太低)

AVL樹、紅黑樹以及B樹介紹