(1)二叉樹簡介: 二叉查找樹也稱爲有序二叉查找樹,知足二叉查找樹的通常性質,是指一棵空樹具備以下性質:數據庫
一、任意節點左子樹不爲空,則左子樹的值均小於根節點的值;數據結構
二、任意節點右子樹不爲空,則右子樹的值均大於於根節點的值;性能
三、任意節點的左右子樹也分別是二叉查找樹; 優化
四、沒有鍵值相等的節點;
3d
上圖爲一個普通的二叉查找樹,按照中序遍歷的方式能夠從小到大的順序排序輸出:二、三、五、六、七、8。 對上述二叉樹進行查找,如查鍵值爲5的記錄,先找到根,其鍵值是6,6大於5,所以查找6的左子樹,找到3;而5大於3,再找其右子樹;一共找了3次。若是按二、三、五、六、七、8的順序來找一樣需求3次。用一樣的方法在查找鍵值爲8的這個記錄,此次用了3次查找,而順序查找須要6次。計算平均查找次數得:順序查找的平均查找次數爲(1+2+3+4+5+6)/ 6 = 3.3次,二叉查找樹的平均查找次數爲(3+3+3+2+2+1)/6=2.3
指針
(2)侷限性及應用
cdn
一個二叉查找樹是由n個節點隨機構成,因此,對於某些狀況,二叉查找樹會退化成一個有n個節點的線性鏈表。以下圖:
blog
如上圖,若是咱們的根節點選擇是最小或者最大的數,那麼二叉查找樹就徹底退化成了線性結構。上圖中的平均查找次數爲(1+2+3+4+5+5)/6=3.16次,和順序查找差很少。顯然這個二叉樹的查詢效率就很低,所以若想最大性能的構造一個二叉查找樹,須要這個二叉樹是平衡的、從而引出了一個新的定義-平衡二叉樹AVL
排序
(1)簡介索引
AVL樹是帶有平衡條件的二叉查找樹,通常是用平衡因子差值判斷是否平衡並經過旋轉來實現平衡,左右子樹樹高不超過1,和紅黑樹相比,它是嚴格的平衡二叉樹,平衡條件必須知足(全部節點的左右子樹高度差不超過1)。無論咱們是執行插入仍是刪除操做,只要不知足上面的條件,就要經過旋轉來保持平衡,而旋轉是很是耗時的,由此咱們能夠知道AVL樹適合用於插入刪除次數比較少,查找多的狀況。
從上面是一個普通的平衡二叉樹,這張圖咱們能夠看出,任意節點的左右子樹的平衡因子差值都不會大於1。
(2)侷限性
因爲維護這種高度平衡所付出的代價比從中得到的效率收益還大,故而實際的應用很少,更多的地方是用追求局部而不是很是嚴格總體平衡的紅黑樹。固然,若是應用場景中對插入刪除不頻繁,只是對查找要求較高,那麼AVL仍是較優於紅黑樹。
(1)簡介
一種二叉查找樹,但在每一個節點增長一個存儲位表示節點的顏色,能夠是red或black。經過對任何一條從根到葉子的路徑上各個節點着色的方式的限制,紅黑樹確保沒有一條路徑會比其它路徑長出兩倍。它是一種弱平衡二叉樹(因爲是弱平衡,能夠推出,相同的節點狀況下,AVL樹的高度低於紅黑樹),相對於要求嚴格的AVL樹來講,它的旋轉次數變少,因此對於搜索、插入、刪除操做多的狀況下,咱們就用紅黑樹
(2)性質
一、每一個節點非紅即黑;
二、根節點是黑的;
三、每一個葉節點(葉節點即樹尾端NULL指針或NULL節點)都是黑的;
四、若是一個節點是紅的,那麼它的兩兒子都是黑的;
五、對於任意節點而言,其到葉子點樹NULL指針的每條路徑都包含相同數目的黑節點;
六、每條路徑都包含相同的黑節點;
(3)應用
一、普遍用於C++的STL中,Map和Set都是用紅黑樹實現的;
二、著名的Linux進程調度Completely Fair Scheduler,用紅黑樹管理進程控制塊,進程的虛擬內存區域都存儲在一顆紅黑樹上,每一個虛擬地址區域都對應紅黑樹的一個節點,左指針指向相鄰的地址虛擬存儲區域,右指針指向相鄰的高地址虛擬地址空間;
三、IO多路複用epoll的實現採用紅黑樹組織管理sockfd,以支持快速的增刪改查;
四、Nginx中用紅黑樹管理timer,由於紅黑樹是有序的,能夠很快的獲得距離當前最小的定時器;
五、Java中TreeMap的實現;
說了上述的三種樹:二叉查找樹、AVL和紅黑樹,彷佛咱們尚未摸到MySQL爲何要使用B+樹做爲索引的實現,不要急,接下來咱們就先探討一下什麼是B樹
(1)簡介
咱們在MySQL中的數據通常是放在磁盤中的,讀取數據的時候確定會有訪問磁盤的操做,磁當大規模數據存儲到磁盤中的時候,顯然是一個很是花費時間的過程,能夠經過B樹進行優化,提升磁盤讀取時定位的效率。 爲何B類樹能夠進行優化呢?咱們能夠根據B類樹的特色,構造一個多階的B類樹,而後在儘可能多的在結點上存儲相關的信息,保證層數儘可能的少,以便後面咱們能夠更快的找到信息,磁盤的I/O操做也少一些,並且B類樹是平衡樹,每一個結點到葉子結點的高度都是相同,這也保證了每一個查詢是穩定的。與紅黑樹相比,在相同的的節點的狀況下,一顆B/B+樹的高度遠遠小於紅黑樹的高度(在下面B/B+樹的性能分析中會提到)。B/B+樹上操做的時間一般由存取磁盤的時間和CPU計算時間這兩部分構成,而CPU的速度很是快,因此B樹的操做效率取決於訪問磁盤的次數,關鍵字總數相同的狀況下B樹的高度越小,磁盤I/O所花的時間越少。 注意B-樹就是B樹
(2)B樹的性質
一、定義任意非葉子結點最多隻有M個兒子,且M>2;
二、根結點的兒子數爲[2, M];
三、除根結點之外的非葉子結點的兒子數爲[M/2, M];
四、每一個結點存放至少M/2-1(取上整)和至多M-1個關鍵字;(至少2個關鍵字)
五、非葉子結點的關鍵字個數=指向兒子的指針個數-1;
六、非葉子結點的關鍵字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];
七、非葉子結點的指針:P[1], P[2], …, P[M];其中P[1]指向關鍵字小於K[1]的子樹,P[M]指向關鍵字大於K[M-1]的子樹,其它P[i]指向關鍵字屬於(K[i-1], K[i])的子樹;
八、全部葉子結點位於同一層;
這裏只是一個簡單的B樹,在實際中B樹節點中關鍵字不少的,上面的圖中好比35節點,35表明一個key(索引),而小黑塊表明的是這個key所指向的內容在內存中實際的存儲位置,是一個指針。
(1)簡介
B+樹是應文件系統所需而產生的一種B樹的變形樹(文件的目錄一級一級索引,只有最底層的葉子節點(文件)保存數據)非葉子節點只保存索引,不保存實際的數據,數據都保存在葉子節點中,這不就是文件系統文件的查找嗎? 咱們就舉個文件查找的例子:有3個文件夾a、b、c, a包含b,b包含c,一個文件yang.c,a、b、c就是索引(存儲在非葉子節點), a、b、c只是要找到的yang.c的key,而實際的數據yang.c存儲在葉子節點上。 全部的非葉子節點均可以當作索引部分
(2)B+樹的性質(下面提到的都是和B樹不相同的性質)
一、非葉子節點的子樹指針與關鍵字個數相同
二、非葉子節點的子樹指針p[i],指向關鍵字值屬於[k[i],k[i+1]]的子樹.(B樹是開區間,也就是說B樹不容許關鍵字重複,B+樹容許重複);
三、爲全部葉子節點增長一個鏈指針;
四、全部關鍵字都在葉子節點出現(稠密索引). (且鏈表中的關鍵字剛好是有序的);
五、非葉子節點至關因而葉子節點的索引(稀疏索引),葉子節點至關因而存儲(關鍵字)數據的數據層;
六、更適合於文件系統
非葉子節點(好比5,28,65)只是一個key(索引),實際的數據存在葉子節點上(5,8,9)纔是真正的數據或指向真實數據的指針。
(3)應用
一、B和B+樹主要用在文件系統以及數據庫作索引,好比MySQL;
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+樹的內部節點並無指向關鍵字具體信息的指針,所以其內部節點相對B樹更小,若是把全部同一內部節點的關鍵字存放在同一盤塊中,那麼盤塊所能容納的關鍵字數量也越多,一次性讀入內存的須要查找的關鍵字也就越多,相對IO讀寫次數就下降了。
二、B+樹的查詢效率更加穩定:因爲非終結點並非最終指向文件內容的結點,而只是葉子結點中關鍵字的索引。因此任何關鍵字的查找必須走一條從根結點到葉子結點的路。全部關鍵字查詢的路徑長度相同,致使每個數據的查詢效率至關。
三、因爲B+樹的數據都存儲在葉子結點中,分支結點均爲索引,方便掃庫,只須要掃一遍葉子結點便可,可是B樹由於其分支結點一樣存儲着數據,咱們要找到具體的數據,須要進行一次中序遍歷按序來掃,因此B+樹更加適合在區間查詢的狀況,因此一般B+樹用於數據庫索引。
PS:我在知乎上看到有人是這樣說的,我感受說的也挺有道理的:
他們認爲數據庫索引採用B+樹的主要緣由是:B樹在提升了IO性能的同時並無解決元素遍歷的我效率低下的問題,正是爲了解決這個問題,B+樹應用而生。B+樹只須要去遍歷葉子節點就能夠實現整棵樹的遍歷。並且在數據庫中基於範圍的查詢是很是頻繁的,而B樹不支持這樣的操做或者說效率過低。