內心有點B樹

[TOC]node

在說B樹以前最好先看看2-3樹, 2-3樹是B樹的一種特例, 什麼B樹, B樹就是2-3樹, 2-3-4 樹 , 2-3-4-5... 樹的統稱, 而B+樹又是B樹的一種變形數組

性質:

  • 什麼是二節點, 三節點...?

二三節點

像上圖那樣,能夠有兩個子節點的節點叫作二節點, 能夠有三個子節點的節點叫作三節點, 而且, 對於二節點來講,能夠有節點,也能夠,沒有節點, 一樣對三節點來講,要麼有三個節點要麼有沒節點, 只有這兩種狀況(不能只有一個節點)性能

若是一棵樹中最大的節點數爲m, 咱們就稱它是m階的B樹設計

注意點就是B樹的全部的子節點所有在同一層, 若是不在同一層了,它確定不是B樹,下文中會講解調整的技巧3d

特性:code

假設每個節點能存儲的元素的個數爲xblog

​ 對於根節點來講: 根節點能存儲的元素的範圍是 1<= x ⇐ m-1 (m爲階數)排序

​ 對於非根節點來講: 他能存儲的元素的個數是  ⌈ m/2 ⌉ -1 ⇐ x ⇐ m-1索引

像下面這樣, 若是當前節點有子節點的話, 子節點的個數是當前節點中存儲的元素數+1內存

性質

推論:

若是當前節點是根節點, 那麼, 當前節點的子節點的個數爲x: 2 ⇐ x ⇐ m

若是當前節點是非根節點, 那麼, 當前節點的子節點的個數x :  ⌈ m/2 ⌉ ⇐ x ⇐ m

**例子: **

好比當m=3時, 也就是說,當前樹爲3階B樹, 經過上面的公式計算它的非根節點的個數是 ⌈ m/2 ⌉ ⇐ x ⇐ 3 即 [2,3] , 此時這棵樹就是咱們所說的2-3樹

構建與上溢:

假設咱們有這樣一個數組: [6,10,4,14,5,11,15,3,2,12,1,7,8] 下面經過畫圖將它轉換成一個3階B樹

在轉化的過程當中, 嚴格遵循上面的特性:

先添加node6, 對於二三樹來講,最多的節點數是三節點的狀態, 因而咱們繼續添加node10, 獲得的下圖

s1

由於node4比node6小, 因此咱們想把node4添加到node6的左邊, 可是此時4,6,10, 三個節點排放在一塊兒就是4節點,不知足2-3的要求,因此咱們須要進行上溢調整 , (或者說,當一個節點中存儲的元素的個數=m時, 進行上溢處理)

第一步: 先求出須要上溢的節點的中間元素的位置 k (就是下面node6的位置2)

第二步: 將k位置的元素向上移動和父節點合併, 沒有父節點,就讓他當父節點

第三步: 將[0-k-1] 和 [k-1,m-1] 位置的元素分裂成上溢節點的左右節點

每次上到父節點時,就會致使樹長高, 還須要注意的地方就是上溢可能會致使當前節點的父節點滿了, 這時候重複這個過程,對其父節點進行上溢的操做

s2

繼續添加node5,和node14, 一樣能夠直接添加進去,且不會打破2-3的平衡

s3

繼續添加node11, 按照正常的排序,咱們會將他排在node10的右邊,可是此時一樣,一個節點中三個元素,說明這個節點是4節點, 不符合2-3的三節點特性, 所以須要上溢, 一樣是選出中間位置的11,而後和它的上一個父節點進行合併, 剩下的node10, node14當成這個node11的左右子節點

s4

繼續添加node15, 按照大小,咱們將他排放在node14的右邊,且知足二三樹的要求

s5

繼續添加node3, 按照順序應該將node3放在node4的左邊,可是這個節點裏面就回顯345三個元素, 也就是說這個節點實際上是4節點,所以咱們將node4上溢, 和它的父節點合併, 而後咱們又發現, 上溢到根節點中,根節點就有了三個元素, 所以咱們進行繼續上溢, 因而樹就會長高一層

s6

繼續咱們插入node2, 按照大小它被放在node3的左邊, 同時樹也不會被打破平衡

s7

接着咱們插入node12, 一樣按照順序它應該被插入在node14的左邊, 可是此時,這個節點中又出現了三個元素, 又出現了4節點, 咱們從新進行上溢出, 一樣是找到 12 14 15 中間的14去和父節點node11進行合併,下圖

s8

用和上面相反的過程咱們能夠插入node1

s9

一樣插入s10這個節點, 一樣咱們會發現7,8,10三個元素在同一個節點上, 所以咱們須要上溢, 而後8,11,14又在同一個節點上, 繼續進行上溢, 而後獲得下圖

s10

刪除與下溢

刪除一個節點:

  • 若是你想刪除的節點在子節點上, 那麼就直接進行刪除
  • 若是你想刪除的節點在根節點行, 就找到這個根節點中這個元素的前驅或者後繼,替換根節點中這個元素的位置,而後刪除這個用來替換的葉子節點

刪除2

好比咱們想刪除60這個節點

因而咱們找到node60的前驅節點, 怎麼找呢? 從node60開始,往左走一次, 而後往右走到頭

若是咱們想找到node60的後繼節點怎麼找呢? 從node60開始,往右走一步,而後往左走到頭

  • 實際上每次被刪除的節點都是葉子節點
  • 每次添加進來的元素,也一定在葉子節點上

underflow下溢

溢出1

上溢的話,就是節點會往上走,下溢正好是相反的過程, 好比說,上樹m=5, 即5階的B樹, 咱們想刪除上面的node22, 可是node22被刪除後,就只剩下了node24, 這時候就違背了B樹的定義, 每個節點中至少要有 ⌈ m/2 ⌉ -1個元素,即 ⌈ 5/2 ⌉ -1 = 2 個元素

下溢節點中元素的數量一定是 ⌈ 5/2 ⌉ -2

  • 狀況1: 向兄弟節點借元素

下溢1

咱們在刪除了根節點的右子樹中的元素,直接致使了這個節點中的元素個數爲 ⌈ m/2 ⌉ -2 , 可是B樹中要求的是至少爲 ⌈ m/2 ⌉ -1 , 咱們就向它的兄弟節點借元素, 由於它的兄弟節點的元素個數比 最低要求還大1, 因而向下面這樣變更,讓根節點中間的元素,放到根節點右子節點的最左邊, 根節點左節點的最右側的元素放到根節點的中間位置, (注意爲了避免打破樹的平衡,咱們須要講nodeT也倒換過去)

下溢2

  • 狀況2: 兄弟節點不夠 ⌈ m/2 ⌉ -1

下溢3

進行元素的合併, 像下面這樣,須要注意的地方就是此時父節點的元素中個數可能不足夠 ⌈ m/2 ⌉ -1,所以咱們重複這個過程

下溢4

B樹的特徵

B樹又稱爲多路搜索樹, 是爲不一樣的存儲設備設計的平衡查找樹,

前面一片博文中咱們知道, AVL樹確實能中和鏈式存儲結構和順序存儲結果的優缺點, 在添加和刪除方面都能表現出優越的性能,這裏咱們 能夠將B樹理解成是平衡二叉樹的升級版

B樹不必定只有兩個叉, 可是B樹和二叉平衡樹同樣都是有序的, 即當咱們按照中序遍歷它樹, 能夠獲得有序數列, 並且B樹比AVL樹更胖,更矮, ,所以樹越高, IO次數就會越大,可是也帶來了更多的優點, 每次在磁盤上讀取數據時,都會進行一次預讀, 使用B樹能夠很好的實現這個特性

AVL樹的磁盤IO性能

avl樹

假設使用AVL樹創建索引, 如上圖, 索引文件不會被保存在內存中, (避免索引文件過大,致使內存的溢出), 因而咱們若是想根據索引讀取數據, 瓶頸就在每次讀取索引文件和磁盤之間的IO上

假設咱們有上面的樹, 而後咱們查詢node10, 會和磁盤進行以下幾回IO

第一次與磁盤的IO : 讀取node4, 往右走

第二次與磁盤的IO : 讀取node8, 往右走

第三次與磁盤的IO : 讀取node9, 往右走

第四次與磁盤的IO : 定位 node10, 結束

雖然使用AVL樹創建索引並非最好的選擇, 可是和全表掃描,進行10次IO相比, 效率仍是很明顯的高效

B樹的磁盤IO性能

B樹磁盤性能

一樣咱們想查詢node10

第一次與磁盤的IO : 讀取node4, 往右走

第二次與磁盤的IO : 讀取node6,node8, 往右走

第三次與磁盤的IO : 讀取node9,定位到node10

很明顯,B樹會比AVl樹更矮, 當存儲的數據超級龐大時, 它甚至能夠比AVL樹減小一半的IO次數

一樣, B樹的範圍查詢效率比較低

B+ 樹

B+樹

**概念: ** 在上樹中, 只有最後一行叫作非葉子節點, 其餘的都叫作葉子節點

B+樹,是B樹的一種變體,查詢性能更好。

m階的B+樹的特徵:

  1. B+樹的非葉子節點只用來存儲索引信息,而不存儲數據,(B樹中每個節點中會存儲數據), 這樣的會B+樹就會在內存中創建龐大的索引體系, 使得他的檢索速度超快
  2. 全部項鍊的葉子節點使用鏈表這種結構進行結合, 有前後的順序, 從而使得它的範圍查詢效率很高

B+樹相比於B樹的查詢優點:

  1. B+樹的中間節點不保存數據,因此磁盤頁能容納更多節點元素,更「矮胖」;
  2. B+樹查詢必須查找到葉子節點,B樹只要匹配到便可不用管元素位置,所以B+樹查找更穩定(並不慢);
  3. 對於範圍查找來講,B+樹只需遍歷葉子節點鏈表便可,b樹卻須要重複地中序遍歷

缺點: 會用冗餘的節點, 比較佔硬盤的大小, (可是這些冗餘的節點都是索引,會大大提升查詢速度)

相關文章
相關標籤/搜索