從二叉查詢樹到B*樹,一文搞懂搜尋樹的演進!|金三銀四系列
對於準備面試這篇再適合不過了!詳細講解了從BST、AVL、紅黑樹、B樹、B+樹最後到B*樹的演進過程,以及各種結構的優劣。
在面試中,面試官很容易丟擲這樣的問題:為什麼MySQL多種資料引擎要用B+樹不用別的資料結構呢?為什麼紅黑樹結構在計算機中記憶體中被廣泛應用?
如果你沒有這樣體系性的思考過這些問題,那非常應該看這篇文章。
本文將從二分查詢說起,詳細講解搜尋樹型結構針對特定問題而產生的演進,讓你知其然更知其所以然。
二分查詢
對於查詢資料,不得不提的一種基礎演算法就是二分法,很多資料結構的查詢演算法核心思想都是二分法,其查詢效率也經常會用來和二分法來做對比。二分法的時間複雜度為O(logN),這是一個非常優秀的時間複雜度,其效率僅次於常數時間複雜度 O(1)。
二分查詢的實現思路是這樣的:
對資料集進行排序 找到資料集的中間節點,判斷是否為查詢的值,等於直接返回。 根據與中間節點大小的比較結果,確定收縮查詢區間範圍是中間節點的左邊還是右邊。 重複上述 2、3 過程繼續查詢。
從其實現思路來看,有兩個點很重要:一是可以保證資料的有序性,二是適合進行資料分段儲存,方便縮小區間查詢。所以根據這種思路,演化出了兩個不同的路線,樹和跳錶兩種資料結構。
二叉搜尋樹BST
二叉搜尋樹(BST,Binary Search Tree)(又叫二叉查詢樹)是一棵空樹,或者是具有下列性質的二叉樹:
若它的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值 若它的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值 它的左、右子樹也分別為二叉排序樹。
二叉搜尋樹的問題
二叉搜尋樹符合了使用二分法查詢資料的要求,但是有個問題:因為插入順序的不同,二叉樹的高度不穩定,極端情況下可能變成連結串列(就是插入的資料是有序的,遞增或者遞減)。這就成了線性查詢,時間複雜度最多變成O(n),查詢效率不穩定。
為了解決這個問題,就產生了各種樹的平衡演算法,保證樹的節點高度不會差太多。所以就有了AVL樹(平衡二叉樹)和紅黑樹等新的資料結構。
AVL樹
平衡二叉樹全稱叫做平衡二叉搜尋(排序)樹,AVL樹是最早的平衡二叉樹之一。
為了解決一般的二叉搜尋樹存在的問題,即根節點到葉子結點的高度相差太多,查詢效率不問題,並且極端情況有成為連結串列的可能。所以AVL樹具有以下特點:
它是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1, 左右兩個子樹 也都是一棵平衡二叉樹。
在AVL樹中,任何節點的兩個子樹的高度最大差別為 1
,所以它也被稱為平衡二叉樹 。
AVL樹查詢、插入和刪除在平均和最壞情況下都是O(LogN)。
AVL 什麼意思 ?AVL 是大學教授 G.M. Adelson-Velsky 和 E.M. Landis 名稱的縮寫,他們提出的平衡二叉樹的概念,為了紀念他們,將 平衡二叉樹 稱為 AVL樹。
與普通二叉搜尋樹的區別的是,它在插入和刪除節點的時候,會根據需要進行左旋或者右旋,來保證二叉樹的平衡,示意圖如下。這不是本文的討論重點,感興趣自己可以研究。
AVL樹的問題
AVL樹高度的平衡情況固然很好,但這是有代價的。為了維持平衡,其旋轉是非常耗時的。
AVL實現平衡的關鍵在於旋轉操作:插入和刪除可能破壞二叉樹的平衡,此時需要透過一次或多次樹旋轉來重新平衡這個樹。當插入資料時,最多隻需要兩次旋轉(單旋轉或雙旋轉);但是當刪除資料時,會導致樹失衡,AVL需要維護從被刪除節點到根節點這條路徑上所有節點的平衡,旋轉的量級為O(lgn)。
由於旋轉的耗時,AVL樹在刪除資料時效率很低;在刪除操作較多時,維護平衡所需的代價可能高於其帶來的好處,因此AVL實際使用並不廣泛。
場景:windows對程式地址空間的管理用到了AVL樹。
針對於這種情況,紅黑樹對其做了最佳化。
紅黑樹RB-Tree
紅黑樹是一種自平衡的二叉查詢樹,是一種高效的查詢樹。它是由 Rudolf Bayer 於1978年發明,在當時被稱為平衡二叉 B 樹(symmetric binary B-trees)。後來,在1978年被 Leo J. Guibas 和 Robert Sedgewick 修改為如今的紅黑樹。紅黑樹具有良好的效率,它可在 O(logN) 時間內完成查詢、增加、刪除等操作。
紅黑樹是一種接近平衡的二叉樹(說它是接近平衡因為它並沒有像AVL樹的平衡因子的概念,它只是靠著滿足紅黑節點的5條性質來維持一種接近平衡的結構,進而提升整體的效能,並沒有嚴格的卡定某個平衡因子來維持絕對平衡)。
特性
一棵紅黑樹同時滿足以下特性:
節點是紅色或黑色 根是黑色 葉子節點(外部節點,空節點)都是黑色,這裡的葉子節點指的是最底層的空節點(外部節點),下圖中的那些null節點才是葉子節點,null節點的父節點在紅黑樹裡不將其看作葉子節點 紅色節點的子節點都是黑色 紅色節點的父節點都是黑色 從根節點到葉子節點的所有路徑上不能有 2 個連續的紅色節點 從任一節點到葉子節點的所有路徑都包含相同數目的黑色節點
紅黑樹的查詢,插入和刪除操作,時間複雜度都是O(logN)。
紅黑樹解決了AVL樹什麼問題
AVL的左右子樹高度差不能超過1,每次進行插入/刪除操作時,幾乎都需要透過旋轉操作保持平衡 在頻繁進行插入/刪除的場景中,頻繁的旋轉操作使得AVL的效能大打折扣 紅黑樹透過犧牲嚴格的平衡,換取插入/刪除時少量的旋轉操作,整體效能優於AVL。紅黑樹插入時的不平衡,不超過兩次旋轉就可以解決;刪除時的不平衡,不超過三次旋轉就能解決 紅黑樹的紅黑規則,保證最壞的情況下,也能在O(log₂n)時間內完成查詢操作。
紅黑樹和AVL樹的效率對比:
如果插入一個node引起了樹的不平衡,AVL樹和紅黑樹都是最多隻需要2次旋轉操作,即兩者都是O(1);但是在刪除node引起樹的不平衡時,最壞情況下,AVL需要維護從被刪node到root這條路徑上所有node的平衡性,因此需要旋轉的量級O(logN),而紅黑樹最多隻需3次旋轉,只需要O(1)的複雜度。 其次,AVL樹的結構相較紅黑樹來說更為平衡,在插入和刪除node更容易引起Tree的不平衡,因此在大量資料需要插入或者刪除時,AVL需要rebalance的頻率會更高。因此,紅黑樹在需要大量插入和刪除node的場景下,效率更高。自然,由於AVL高度平衡,因此AVL的search效率更高。 map的實現只是折衷了兩者在search、insert以及delete下的效率。總體來說,紅黑樹的統計效能是高於AVL的。
最壞情況下,AVL樹有最多O(logN)次旋轉,而紅黑樹最多三次。
場景:
紅黑樹的應用就很多了。
epoll在核心中的實現,用紅黑樹管理事件塊。 nginx中,用紅黑樹管理timer等。 Java1.8版本後的的hashMap中使用連結串列+紅黑樹解決雜湊衝突問題,Java中的TreeMap使用紅黑樹儲存排序鍵值對。 著名的linux程式排程Completely Fair Scheduler,用紅黑樹管理程式控制塊。
紅黑樹的問題
雖然紅黑樹是一種已經被效能最佳化了的自平衡的二叉查詢樹,插入修改效率和查詢效率得到了平衡,但他依然存在一些問題。
依舊在插入和刪除時需要對節點進行旋轉,頻繁修改資料的場景影響效率。 紅黑樹畢竟是一種二叉樹,當資料量很大時,樹的高度會變得很大,查詢時經過的節點過多,效率變低。 紅黑樹在記憶體中表現優秀,但因為樹的高度的問題,當使用磁碟等輔助儲存裝置讀寫資料時(如MySQL等資料庫),會導致資料在磁碟中散佈分散,並且IO次數過多,效率變低。 適合單個查詢,對於資料查詢中常見的範圍查詢場景,無法很好支援。
針對於上述問題,有了天生為磁碟儲存而生的B樹。
B樹
B樹是一種多路搜尋樹,又名平衡多路查詢樹(查詢路徑不只兩個),與二叉樹相比,B樹的每個非葉節點可以有多個子樹。因此,當總節點數量相同時,B樹的高度遠遠小於AVL樹和紅黑樹(B樹是一顆“矮胖子”),磁碟IO次數大大減少。資料庫索引技術裡大量使用者B樹和B+樹的資料結構。
定義B樹最重要的概念是階數(Order),對於一顆m階B樹(就是一個節點最多包含幾個子節點),需要滿足以下條件:
每個節點最多包含 m 個子節點。 如果根節點包含子節點,則至少包含 2 個子節點;除根節點外,每個非葉節點至少包含 m/2 個子節點。 擁有 k 個子節點的非葉節點將包含 k - 1 條記錄。 所有葉節點都在同一層中。
度數:在樹中,每個節點的子節點(子樹)的個數就稱為該節點的度 (degree)。
階數:階(order)定義為一個節點最多可以有多少個元素。
如下圖,這是一個2階3度的B樹。
場景:MongoDB索引。
B樹的優勢
B樹相對平衡二叉樹在節點空間的利用率上進行改進,B樹在每個節點儲存更多的資料,減少了樹的高度,從而提升了查詢的效能。
B樹的優勢除了樹高小,還有對訪問區域性性原理的利用。
所謂區域性性原理,是指當一個資料被使用時,其附近的資料有較大機率在短時間內被使用。B樹將鍵相近的資料儲存在同一個節點,當訪問其中某個資料時,資料庫會將該整個節點讀到快取中;當它臨近的資料緊接著被訪問時,可以直接在快取中讀取,無需進行磁碟IO;換句話說,B樹的快取命中率更高。
在資料庫應用中,B樹的每個節點儲存的資料量大約為4K, 這是因為考慮到磁碟資料儲存是採用塊的形式儲存的,每個塊的大小為4K,每次對磁碟進行IO資料讀取時,同一個磁碟塊的資料會被一次性讀取出來,所以每一次磁碟IO都可以讀取到B樹中一個節點的全部資料。
對於順數插入的資料,B樹的結構優勢可以使其在記憶體中順序排列,存貯到同一個磁碟頁中,順序插入對磁碟的利用率和讀取效率都非常友好。
場景:MySQL的InnbDB 索引。
B樹的問題
B樹雖然解決了磁碟儲存的問題,但是在查詢範圍資料時依舊不夠優秀,比如你要查詢1-5的資料,必須按照樹的中順遍歷來訪問各個節點。
對於這個問題,B+樹對其進行了最佳化。
B+樹
B+樹是在B樹的基礎上又一次的改進,其主要對兩個方面進行了提升,一方面是查詢的穩定性,另外一方面是在資料排序方面更友好。
B+樹也是多路平衡查詢樹,其特性主要有以下4點:
B樹中每個節點(包括葉節點和非葉節點)都儲存真實的資料,B+樹中只有葉子節點儲存真實的資料,非葉節點只儲存鍵。在MySQL中,這裡所說的真實資料,可能是行的全部資料(如Innodb的聚簇索引),也可能只是行的主鍵(如Innodb的輔助索引),或者是行所在的地址(如MyIsam的非聚簇索引)。 B樹中一條記錄只會出現一次,不會重複出現,而B+樹的鍵則可能重複重現——一定會在葉節點出現,也可能在非葉節點重複出現。 B+樹的葉節點之間透過雙向連結串列連結。B+樹葉子節點的關鍵字從小到大有序排列,左邊結尾資料都會儲存右邊節點開始資料的指標。因為葉子節點都是有序排列的,所以B+樹對於資料的排序有著更好的支援。 B+樹非葉子節點的子節點數=關鍵字數,或者非葉節點的關鍵字數=子節點數-1(這裡有兩種演算法的實現方式),雖然他們資料排列結構不一樣,但其原理還是一樣的Mysql 的B+樹是用第一種方式實現)。
第一種演算法:
第二種演算法:
B+樹和B樹的對比
1、B+樹的層級更少:相較於B樹B+每個非葉子節點儲存的關鍵字數更多,所以每個磁碟塊儲存的資料更多,樹的層級更少所以查詢資料更快
2、B+樹查詢速度更穩定:B+所有關鍵字資料地址都存在葉子節點上,所以每次查詢的次數都相同所以查詢速度要比B樹更穩定
3、B+樹天然具備排序功能:所有關鍵字都出現在葉子結點的連結串列中(稠密索引),B+樹所有的葉子節點資料構成了一個有序連結串列,在查詢大小區間的資料時候更方便,資料緊密性很高,快取的命中率也會比B樹高。
4、B+樹全節點遍歷更快:B+樹遍歷整棵樹只需要遍歷所有的葉子節點即可,而不需要像B樹一樣需要對每一層進行遍歷,這有利於資料庫做全表掃描。
B樹相對於B+樹的優點是,如果經常訪問的資料離根節點很近,而B樹的非葉子節點本身存有關鍵字其資料的地址,所以這種資料檢索的時候會要比B+樹快。
5、B+樹更適合檔案索引系統
B+樹的缺點
在B+樹的構建過程中,為了保持樹的平衡,節點的合併拆分是比較耗費時間的,所以B*樹就是在如何減少構建中節點合併和拆分的次數,從而提升樹的資料插入、刪除效能。
B*樹
相對於B+樹,B*樹的不同之處如下:
(1)首先是關鍵字個數限制問題,B+樹初始化的關鍵字初始化個數是cei(m/2),b樹的初始化個數為(cei(2/3m))
(2)B+樹節點滿時就會分裂,而B*樹節點滿時會檢查兄弟節點是否滿(因為每個節點都有指向兄弟的指標),如果兄弟節點未滿則向兄弟節點轉移關鍵字,如果兄弟節點已滿,則從當前節點和兄弟節點各拿出1/3的資料建立一個新的節點出來;
B*樹 與B+樹對比
在B+樹的基礎上因其初始化的容量變大,使得節點空間使用率更高,而又存有兄弟節點的指標,可以向兄弟節點轉移關鍵字的特性使得B*樹額分解次數變得更少。
總結
對於上述的演進過程,這裡給出一個簡要總結,如下圖。建議收藏!
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70027826/viewspace-2941867/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 滿二叉樹、完全二叉樹、平衡二叉樹、二叉搜尋樹(二叉查詢樹)和最優二叉樹二叉樹
- 資料結構之樹結構概述(含滿二叉樹、完全二叉樹、平衡二叉樹、二叉搜尋樹、紅黑樹、B-樹、B+樹、B*樹)資料結構二叉樹
- B樹(多路查詢樹)
- 查詢|有序表折半查詢判定樹|二叉排序樹|3階B-樹排序
- 資料結構中的樹(二叉樹、二叉搜尋樹、AVL樹)資料結構二叉樹
- 二叉搜尋樹
- 二叉樹 & 二叉查詢樹二叉樹
- 一文搞定二叉排序(搜尋)樹排序
- 二叉樹、B樹以及B+樹二叉樹
- 平衡二叉樹,B樹,B+樹二叉樹
- 二叉查詢樹(二叉排序樹)排序
- 多路查詢樹(2-3 樹、2-3-4 樹、B 樹、B+ 樹)
- 二叉搜尋樹和二叉樹的最近公共祖先二叉樹
- Day20 | 654.最大二叉樹 、 617.合併二叉樹 、 700.二叉搜尋樹中的搜尋 98.驗證二叉搜尋樹二叉樹
- 手擼二叉樹——二叉查詢樹二叉樹
- 平衡二叉查詢樹:紅黑樹
- 二叉查詢樹和笛卡爾樹
- js實現完全排序二叉樹、二叉搜尋樹JS排序二叉樹
- 查詢二叉樹二叉樹
- 二叉查詢樹
- 【資料結構】【二叉樹】四、二叉搜尋樹的特性(不斷補充)資料結構二叉樹
- 實現二叉搜尋樹的新增,查詢和刪除(JAVA)Java
- 二叉搜尋樹的操作集
- 二叉搜尋樹的結構
- JavaScript 二叉搜尋樹以及實現翻轉二叉樹JavaScript二叉樹
- 二分搜尋樹系列之[查詢(Search)-包含(Contain)]AI
- 二分搜尋樹系列之「查詢(Search)-包含(Contain)」AI
- 96. 不同的二叉搜尋樹
- javascript實現二叉搜尋樹JavaScript
- 有序表和搜尋二叉樹二叉樹
- B樹查詢,磁碟查詢資料
- Java中在二叉搜尋樹中查詢節點的父節點Java
- B樹(B-tree, 平衡的多路查詢樹)的相關知識
- 咬文嚼圖式的介紹二叉樹、B樹/B-樹二叉樹
- 資料結構:一文看懂二叉搜尋樹 (JavaScript)資料結構JavaScript
- 【資料結構第四周】樹知識點整理(下)【二叉搜尋樹】資料結構
- 二叉查詢樹【二叉排序樹】構建和查詢演算法 PHP 版排序演算法PHP
- 二叉查詢樹的個數