一、二叉查詢樹
(1)二叉樹簡介:
二叉查詢樹也稱為有序二叉查詢樹,滿足二叉查詢樹的一般性質,是指一棵空樹具有如下性質:
1、任意節點左子樹不為空,則左子樹的值均小於根節點的值;
2、任意節點右子樹不為空,則右子樹的值均大於於根節點的值;
3、任意節點的左右子樹也分別是二叉查詢樹;
4、沒有鍵值相等的節點;
上圖為一個普通的二叉查詢樹,按照中序遍歷的方式可以從小到大的順序排序輸出:15、20、30、50、60、70。
對上述二叉樹進行查詢,如查鍵值為30的記錄,先找到根,其鍵值是50,50大於30,因此查詢50的左子樹,找到20;而30大於20,再找其右子樹;一共找了3次。如果按15、20、30、50、60、70的順序來找同樣需求3次。用同樣的方法在查詢鍵值為70的這個記錄,這次用了3次查詢,而順序查詢需要6次。計算平均查詢次數得:順序查詢的平均查詢次數為(1+2+3+4+5+6)/ 6 = 3.3次,二叉查詢樹的平均查詢次數為(3+3+3+2+2+1)/6=2.3次。二叉查詢樹的平均查詢速度比順序查詢來得更快。
(2)侷限性及應用
一個二叉查詢樹是由n個節點隨機構成,所以,對於某些情況,二叉查詢樹會退化成一個有n個節點的線性鏈。
大家看上圖,如果我們的根節點選擇是最小或者最大的數,那麼二叉查詢樹就完全退化成了線性結構。上圖中的平均查詢次數為(1+2+3+4+5+5)/6=3.16次,和順序查詢差不多。顯然這個二叉樹的查詢效率就很低,因此若想最大效能的構造一個二叉查詢樹,需要這個二叉樹是平衡的(這裡的平衡從一個顯著的特點可以看出這一棵樹的高度比上一個輸的高度要大,在相同節點的情況下也就是不平衡),從而引出了一個新的定義-平衡二叉樹AVL。
二、AVL樹
(1)簡介
AVL樹是帶有平衡條件的二叉查詢樹,一般是用平衡因子差值判斷是否平衡並通過旋轉來實現平衡,左右子樹樹高不超過1,和紅黑樹相比,它是嚴格的平衡二叉樹,平衡條件必須滿足(所有節點的左右子樹高度差不超過1)。不管我們是執行插入還是刪除操作,只要不滿足上面的條件,就要通過旋轉來保持平衡,而旋轉是非常耗時的,由此我們可以知道AVL樹適合用於插入刪除次數比較少,但查詢多的情況。
從上面是一個普通的平衡二叉樹,這張圖我們可以看出,任意節點的左右子樹的平衡因子差值都不會大於1。
(2)侷限性
由於維護這種高度平衡所付出的代價比從中獲得的效率收益還大,故而實際的應用不多,更多的地方是用追求區域性而不是非常嚴格整體平衡的紅黑樹。當然,如果應用場景中對插入刪除不頻繁,只是對查詢要求較高,那麼AVL還是較優於紅黑樹。
(3)應用
1、Windows NT核心中廣泛存在;
三、紅黑樹
(1)簡介
一種二叉查詢樹,但在每個節點增加一個儲存位表示節點的顏色,可以是red或black。通過對任何一條從根到葉子的路徑上各個節點著色的方式的限制,紅黑樹確保沒有一條路徑會比其它路徑長出兩倍。它是一種弱平衡二叉樹(由於是若平衡,可以推出,相同的節點情況下,AVL樹的高度低於紅黑樹),相對於要求嚴格的AVL樹來說,它的旋轉次數變少,所以對於搜尋、插入、刪除操作多的情況下,我們就用紅黑樹。
(2)性質
1、每個節點非紅即黑;
2、根節點是黑的;
3、每個葉節點(葉節點即樹尾端NULL指標或NULL節點)都是黑的;
4、如果一個節點是紅的,那麼它的兩兒子都是黑的;
5、對於任意節點而言,其到葉子點樹NULL指標的每條路徑都包含相同數目的黑節點;
6、每條路徑都包含相同的黑節點;
(3)應用
1、廣泛用於C++的STL中,Map和Set都是用紅黑樹實現的;
2、著名的Linux程式排程Completely Fair Scheduler,用紅黑樹管理程式控制塊,程式的虛擬記憶體區域都儲存在一顆紅黑樹上,每個虛擬地址區域都對應紅黑樹的一個節點,左指標指向相鄰的地址虛擬儲存區域,右指標指向相鄰的高地址虛擬地址空間;
3、IO多路複用epoll的實現採用紅黑樹組織管理sockfd,以支援快速的增刪改查;
4、Nginx中用紅黑樹管理timer,因為紅黑樹是有序的,可以很快的得到距離當前最小的定時器;
5、Java中TreeMap的實現;
四、B/B+樹
(1)簡介
B/B+樹是為了磁碟或其它儲存裝置而設計的一種平衡多路查詢樹(相對於二叉,B樹每個內節點有多個分支),與紅黑樹相比,在相同的的節點的情況下,一顆B/B+樹的高度遠遠小於紅黑樹的高度(在下面B/B+樹的效能分析中會提到)。B/B+樹上操作的時間通常由存取磁碟的時間和CPU計算時間這兩部分構成,而CPU的速度非常快,所以B樹的操作效率取決於訪問磁碟的次數,關鍵字總數相同的情況下B樹的高度越小,磁碟I/O所花的時間越少。
注意B-樹就是B樹,-只是一個符號。
(2)B樹的性質
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樹,在實際中B樹節點中關鍵字很多的。
五、B+樹
(1)簡介
B+樹是應檔案系統所需而產生的一種B樹的變形樹(檔案的目錄一級一級索引,只有最底層的葉子節點(檔案)儲存資料)非葉子節點只儲存索引,不儲存實際的資料,資料都儲存在葉子節點中。所有的非葉子節點都可以看成索引部分!
(2)B+樹的性質(下面提到的都是和B樹不相同的性質)
1、非葉子節點的子樹指標與關鍵字個數相同;
2、非葉子節點的子樹指標p[i],指向關鍵字值屬於[k[i],k[i+1]]的子樹.(B樹是開區間,也就是說B樹不允許關鍵字重複,B+樹允許重複);
3、為所有葉子節點增加一個鏈指標;
4、所有關鍵字都在葉子節點出現(稠密索引). (且連結串列中的關鍵字恰好是有序的);
5、非葉子節點相當於是葉子節點的索引(稀疏索引),葉子節點相當於是儲存(關鍵字)資料的資料層;
6、更適合於檔案系統;
非葉子節點(比如5,28,65)只是一個key(索引),實際的資料存在葉子節點上(5,8,9)才是真正的資料或指向真實資料的指標。
(3)應用
1、B和B+樹主要用在檔案系統以及資料庫做索引,比如MySQL;
六、B/B+樹效能分析
n個節點的平衡二叉樹的高度為H(即logn),而n個節點的B/B+樹的高度為logt((n+1)/2)+1;
若要作為記憶體中的查詢表,B樹卻不一定比平衡二叉樹好,尤其當m較大時更是如此。因為查詢操作CPU的時間在B-樹上是O(mlogtn)=O(lgn(m/lgt)),而m/lgt>1;所以m較大時O(mlogtn)比平衡二叉樹的操作時間大得多。因此在記憶體中使用B樹必須取較小的m。(通常取最小值m=3,此時B-樹中每個內部結點可以有2或3個孩子,這種3階的B-樹稱為2-3樹)。
七、為什麼說B+樹比B樹更適合資料庫索引?
1、 B+樹的磁碟讀寫代價更低:B+樹的內部節點並沒有指向關鍵字具體資訊的指標,因此其內部節點相對B樹更小,如果把所有同一內部節點的關鍵字存放在同一盤塊中,那麼盤塊所能容納的關鍵字數量也越多,一次性讀入記憶體的需要查詢的關鍵字也就越多,相對IO讀寫次數就降低了。
2、B+樹的查詢效率更加穩定:由於非終結點並不是最終指向檔案內容的結點,而只是葉子結點中關鍵字的索引。所以任何關鍵字的查詢必須走一條從根結點到葉子結點的路。所有關鍵字查詢的路徑長度相同,導致每一個資料的查詢效率相當。
3、由於B+樹的資料都儲存在葉子結點中,分支結點均為索引,方便掃庫,只需要掃一遍葉子結點即可,但是B樹因為其分支結點同樣儲存著資料,我們要找到具體的資料,需要進行一次中序遍歷按序來掃,所以B+樹更加適合在區間查詢的情況,所以通常B+樹用於資料庫索引。
4、B樹在提高了IO效能的同時並沒有解決元素遍歷的我效率低下的問題。B+樹只需要去遍歷葉子節點就可以實現整棵樹的遍歷。而且在資料庫中基於範圍的查詢是非常頻繁的,而B樹不支援這樣的操作或者說效率太低
本作品採用《CC 協議》,轉載必須註明作者和本文連結