【數據結構】初入數據結構中的B類樹(B Tree , B+ Tree)

初入數據結構中的B類樹(B- Tree , B+ Tree)


如果覺得對你有幫助,能否點個贊或關個注,以示鼓勵筆者呢?!博客目錄 | 先點這裏

  • 前提概念
    • 數據域
    • 指針域
  • B樹
    • 什麼是B樹(B-樹)?
    • B樹的定義
    • B樹的特性
    • B樹的高
  • B+ 樹
    • 什麼是B+樹?
    • B+樹的定義
    • B+樹的小特性
    • 爲什麼B+樹會替代B樹?
  • 爲什麼需要B樹,B+樹?
    • 引文
    • 爲什麼我們需要B類樹?

前提概念


爲了更好的理解B類樹,我們先來簡單的瞭解一下兩個概念

數據域

B類樹的結點中有兩個區域,一個是數據域,一個是指針域

什麼是數據域?

  • 簡單的說,B類樹中每個結點存儲數據的地方,我們就稱之爲數據域
  • 而數據域中存儲的數據通常是一個個的鍵值對,鍵值對就分爲鍵(key)值(value),這裏通常稱鍵爲關鍵字也通常會將關鍵字直接指代整個鍵值對
  • 站在數據庫索引的角度,關鍵字就是用於建立索引的字段值,而對應的值就是該關鍵字所對應的目的數據,可以是主鍵信息,也可以指向實際數據的地址,甚至是完整的一行數據

指針域

什麼是指針域?

  • 我們知道二叉樹在代碼的實現中,通常會有兩個指針,既左孩子指針和右孩子指針。而這兩個指針所在的區域,我們就稱之爲指針域
  • 而存放左右孩子指針的因爲B類樹並不是一棵二叉樹,它是一棵多叉樹。也就說明了,一棵B類樹,可能有多個指針,每個的結點的所有的指針所在的區域就是我們說的指針域啦。概念都是一樣的。

B樹


什麼是B樹(B-樹)?

B-樹,即爲B樹。需要強調的是它們兩者都是同一種樹,並非兩種不一樣的樹。因爲B樹的英文名稱爲B-tree-實際是槓的意思,但是也就不知怎麼的B-tree就被翻譯成爲B-(減)樹

B樹,又名B-樹(B減樹)。它是一棵多路平衡多路查找樹


B樹的定義

一棵m階B樹的定義

  • 結點需要滿足
    (1) 根結點至少有兩個孩子結點
    (2) 樹中每個結點最多有m個孩子結點(m>=2)
    (3) 除根結點和葉子結點外,其他所有結點至少要有 ceil(m/2)個孩子
    (4) 所有的葉子結點都位於同一層(平衡特性

  • 關鍵字需要滿足
    假設每個非葉子結點中包含n個關鍵字信息,關鍵字以k表示,指針以p表示
    (1) Ki (i = 1,…,n)某個非葉子結點的關鍵字,關鍵字需要滿足升序序列排序,k(i-1) < k(i)
    (2) 每個非葉子結點中的關鍵字個數n需要滿足 ceil(m/2) -1 <= n <= m - 1
    (3) 非葉子結點的指針p[1]…p[m]中

    • 第一個指針,p[1]指向的孩子結點的所有關鍵字必然小於當前結點k[1]關鍵字
    • 最後一個指針,p[m]指向的孩子結點的所有關鍵字肯定大於當前結點k[m-1]關鍵字 ;
    • 其他p[i]指向孩子結點所有的關鍵字必然在當前結點(k[i-1],k[i])的開區間範圍內

通俗版解釋一下

  • m就是幾階的含義,m取決結點的容量和相關配置。3階B樹的每個結點分叉數最多爲3,最多三個孩子結點
  • ceil函數是指向上取整的意思,非四捨五入,如ceil(1.5) = 2, ceil(1.2) = 2 ,ceil(3.4) = 4
  • 關鍵字定義(2) 的意思可以通俗的理解成,每個非葉子結點的關鍵字個數肯定小於該結點能擁有的孩子結點樹,最多就存儲m - 1個關鍵字,最少也要存儲ceil(m/2) - 1
  • 關鍵字定義(3) 的意思是某結點最左孩子結點的所有關鍵字肯定小於當前結點最左關鍵字的值,就像下圖的3,5肯定小於8
  • 最右孩子結點的所有關鍵字肯定大於當前結點的最右關鍵字 ; 例如下圖的13肯定大於12和8
  • 而中間的孩子結點的所有關鍵字肯定滿足於一個當前結點關鍵字的開區間範圍(k[i - 1] , k[i]) , 例如下圖的p2孩子結點的9關鍵字肯定位於(8,12)關鍵字之間
    在這裏插入圖片描述
    上圖是一個3階B樹

總之,我們大致的知道,B樹並不是一棵二叉搜索樹,而是一顆具有平衡性質的多路查找樹。同時B樹的定義比較複雜,具有指針域和關鍵字域的概念,一個結點可以存儲多個數據元素,上一段是對結點定義約束,下一段是對結點內部所存儲的關鍵字的約束


B樹的特性

  • 關鍵字集合分佈在整棵樹中
  • 任何一個關鍵字出現且只出現在一個結點中
  • 搜索有可能在非葉子結點結束
  • B樹的查找性能基本等價於平衡二叉樹的二分查找(不考慮IO的情況下)
  • 可以實現自平衡,讓所有葉子結點都在同一個層級

B樹的高

現在有一個問題,關鍵字總數爲n的m階B樹的高是多少?

  • (logceil(m/2)(n+1)/2log_{ceil(m/2)}{(n+1)/2}) + 1

  • 既底數是ceil(m/2),最後的對數結果 + 1就是這顆B樹的高

引用至@百度百科
在這裏插入圖片描述

因爲求B樹,B+樹的高的資料的確是有些少,所以看完之後,說實話,我是一臉懵逼的。的確有挺多問題,我都沒有理解好,比如說B樹含N個關鍵字,爲什麼就有N +1個葉子結點呢?這個是怎麼推出來的。這個問題可以確定的話,中間步驟還是好理解的。但是…但是後面的歸納還是懵了。

所以,想想算了,時間有限,因爲學習性價比,所以還是暫時留給有志之士去解答吧,貧道暫無能爲力


B+ 樹


什麼是B+樹?

B+樹是一種樹型數據結構,通常用於數據庫和操作系統的文件系統中。B+樹的特點是能夠保持數據穩定有序,其插入與修改擁有較穩定的對數時間複雜度。B+樹元素自底向上插入,這與二叉樹恰好相反。
B+樹 - @百度百科


B+樹的定義

B+樹是B樹的變種,其定義基本與B樹相同,除了

  • 非葉子結點的子樹指針與關鍵字個數相同,不像B樹的關鍵字數要少於指針數
  • 非葉子結點的子樹指針p[i]所指向的孩子結點的所有關鍵字處於當前結點關鍵字[k[i],k[i+1])左閉右開的範圍區間內
  • 所有的非葉子結點的關鍵字,都會繼承至其中孩子結點中,是其孩子結點元素的最大值或最小值
  • 非葉子結點僅用來索引,數據都保存在葉子結點中
  • 所有葉子結點的數據均有兩個鏈指針分別指向上一個數據和下一個數據,構成雙向鏈表

在這裏插入圖片描述
B樹和B+樹的區別

  • B+樹,不同於B樹的是,所有數據都存儲在葉子結點中,非葉子結點不存儲任何數據,僅僅是一個索引值。所以B+樹查找的時間複雜度非常穩定,都是從根遍歷到葉子結點,不會在非葉子結點上中斷。

B+樹小特性

因爲B+樹的定義中,所有的非葉子結點的關鍵字,都會繼承至其中孩子結點中,是其孩子結點元素的最大值或最小值,所以就造成了一個特性,根結點的最小值,可能是整棵樹的最小值。根結點的最大值可能是整棵樹的最大值

舉例如下圖:

在這裏插入圖片描述
在這裏插入圖片描述


爲什麼B+樹會替代B樹?

總之呢,在數據庫索引,文件系統等許多場景中,B+樹逐漸替代B樹,這是爲什麼呢?

  • B+樹的磁盤讀寫代價更低
    因爲B+樹的中間結點存儲的都是索引數據,僅僅是一個地址,並非直接的數據,所以同一個結點中(同一個磁盤頁大小),B+樹可容納的關鍵字數會比B樹更多(因爲一個簡單的地址幾乎肯定小於一個直接的數據)。所以同樣的數據量下,B+樹會比B樹更加“矮胖”,樹高更小,所以查詢時需要的IO次數就更少
  • B+樹的查詢效率更加穩定
    因爲B+樹的所有元素都存儲在葉子結點中,而葉子結點都屬於同一層級,每一個B+樹查詢都是從根結點遍歷到葉子結點的過程,所以不管查詢什麼,時間複雜度相比B樹查詢都更加的穩定和近似。
  • B+樹更有利對數據的掃描
    B樹中雖然解決了查詢的效率,但是如果需要查詢一串相鄰的數值,有可能需要回溯來回掃描或是從根結點多次中序遍歷。而B+樹的所有元素都存儲葉子結點,每個葉子結點都有指向下一個結點的指針,直接線性遍歷即可。同樣B+樹也更加的利於做範圍查詢

爲什麼需要B樹,B+樹?


引文

階段一
在瞭解爲什麼需要B類樹之前,我們首先來了解一下,我們爲什麼需要二叉查找樹?
在這裏插入圖片描述
我們爲什麼需要二叉查找樹呢?

  • 簡單的說,就是我們想要更快的從一堆數據中,找到我們想要的數據對吧。
  • 一個n長度的線性數據集合,正確情況下的查找,就是一個一個的遍歷,時間複雜度是O(n), 所以非常慢,這時候我們就引入了二分查找,時間複雜度就從O(n)驟降到O(logn)Wow! NiuBei!!

階段二
我們有了二叉查找樹啦,終於可以快速的查找速度了。但是有一天,我們突然的發現這個二叉查找樹,也變的很慢很慢??怎麼回事?有Bug嗎?!!!
在這裏插入圖片描述
自平衡二叉查找樹
因爲二叉查找樹的不平衡現象,導致最差的查找結果會退化成O(n)的線性遍歷,所以我們要避免出現這麼一個情況。於是自平衡二叉樹就出現了

  • 自平衡二叉查找樹的出現,讓二叉查找樹在不斷的增刪查改中得到平衡的維護。總之一句話,就是不會讓搜索退化成O(n)的線性遍歷,依然保持高性能的O(logn), Wow! NiuBei!!
  • 當然這個階段也有很多的分類哈,比如AVL樹紅黑樹,等等

階段三

  • 鑑於我知識體系的逐漸豐富,我突然想造個數據庫誒,然後陷入造數據庫的龐大工程中…
    嗯,使用起來還可以誒,我真NiuBi。(一段時間後,數據量大了~~)

  • Wo Cao! 這有點慢,怎麼辦?怎麼辦?
    行吧,那就上索引吧,加入索引特性,我的索引就用平衡二叉搜索樹來實現吧,之前感覺很良好AVL樹紅黑樹等隨便一種啦,逃…),

  • 索引新特性上線中
    呀,提升不小呀! 至少比以前快了來不少,看來我還是Niu Bei ! (數據量持續增大中~)

  • Wo Cao! 怎麼有索引還這麼慢?又出Bug了?
    啊,我上索引了,IO操作還這麼頻繁的嘛? IO資源都要炸啦! 涼涼,看來還是我操作系統基礎太差了,不該用平衡二叉樹作爲索引的底層實現呀!


爲什麼我們需要B類樹?

從上面的引文中,我們知道,當我們使用平衡二叉查找樹作爲數據庫索引的底層實現時,會面臨着大量IO操作,從而導致查找性能不佳。

原因一
那麼以平衡二叉查找樹爲底層實現的索引查找爲什麼會面臨這麼大量的IO操作呢?

  • 我們知道平衡二叉查找樹的本質是一棵二叉樹,二叉樹的每個結點最多隻能有兩個孩子結點。樹高爲n的二叉樹最多隻能有(2^n) - 1個結點。

  • 也就是說, 一棵高爲n的平衡二叉查找樹,最多隻能存放(2^n) -1個數據的索引。那麼我們的數據庫如果有1億的數據量,那麼我們索引樹的高大概就有27層,2^27 = 1,3421,7728(1億+)

  • 當我們要在1億個數據中查找我們想要的數據,最差的情況下,就要進行27次判斷,也就是要進行27次IO操作。 這27次IO操作聽上去可能還很少的樣子,但是因爲磁盤讀寫速度很慢(不是內存計算呀),所以27次慢速讀寫累加起來就是一個龐大的耗時操作了。

所以現在我們知道了以平衡二叉查找樹爲底層實現的索引查找爲什麼這麼慢了。而B樹,B+ 樹可以遠遠的降低我們索引樹的高度,同樣的數據量,因爲B樹,B+樹的一個結點可以容納很多很多個關鍵字,同時還可以由很多個孩子結點,從幾個到上千個,所以整體索引樹的高度可能只有 3~4層 的高。27 ->4 什麼概念?簡直是削減呀!這就是我們爲什麼需要B類樹的原因之一啦

  • 簡單的說,B類樹在數據庫索引上的應用性能會比平衡二叉搜索高,並不是B類樹在設計上就比平衡二叉搜索樹牛逼,而是在數據庫讀寫是站在磁盤讀寫基礎上的,追求的是少量的IO操作,而B類樹恰好可以做到這一點。
  • 但是如果要換成以內存計算爲前提的查找,B類樹的性能就不一定要比平衡二叉樹要高了,性能甚至會更低。因爲內存計算中沒有所謂的IO操作。以降低IO操作次數爲賣點之一的B類樹沒有太多的優勢可言了。

原因二

我們知道關係型數據庫,文件管理系統領域,數據主要是存儲在磁盤中的

  • 而磁盤讀取數據是以盤塊(block)爲基本單位的,既磁盤讀取數據一個IO操作就讀取一個盤塊的數據。而我們的B類樹一個結點可以存儲多個關鍵字就恰好可以符合這麼一個磁盤的特性
  • 想想如果磁盤每一個盤塊的數據,恰好對映射到B類樹的一個結點中,既每一個結點所存儲的關鍵字量恰好對應一個磁盤盤塊的數據。同一個結點中關鍵字的線性遍歷,也不需要在磁盤方面跨盤塊進行操作,這樣就又省了一筆IO操作

這就是我們爲什麼需要B類樹的原因之二

總結一下

所以我們可以簡單的總結一下,爲什麼在數據庫,文件管理系統領域,我們需要使用B類樹來替代平衡二叉搜索樹成爲索引的底層實現,主要就是兩個原因:

  • B類樹的一個結點可以存儲多個數據, 可以有多個分支,所以可以大大的降低正棵樹的高度,減少磁盤IO操作
  • B類樹的一個結點可以存儲多個數據的特點,恰好也符合了磁盤的物理構造特性,通過將一個結點最多存儲對應一個物理盤塊的數據,讓同結點數據進行線性遍歷時,不需要跨盤塊進行查詢,同樣也減少了磁盤查詢時間

參考資料


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章