b+樹b*樹
我對b樹這個東西一直不是很理解,今天就這篇部落格,搞懂b樹的來龍去脈。
b樹英文b-tree,有的人居然翻譯成b-樹,我也是醉了。其實這就是個多叉搜尋樹,而二叉搜尋樹是它的特化情況。b樹里存放的是key-value的鍵值對,只不過是根據key進行的組織排序而已。
一顆m階b樹特性如下:
1.任意乙個節點的孩子數量最大是m;
2.除了根節點root和葉子結點外,其他的節點孩子數量是ceil(m / 2),ceil是向上取整。
3.根節點可以最少有兩個孩子,當然如果只有乙個根節點的話咱們不討論
4.每個結點中的關鍵字都按照從小到大的順序排列,每個關鍵字的左子樹中的所有關鍵字都小於它,而右子樹中的所有關鍵字都大於它
5.所有葉子結點都位於同一層
你可能粗略的看完一遍b樹特性,感覺很奇怪。放心,當你看完b樹的構造方法和刪除方法時,你就釋然了。
1.根據要插入的key的值,找到葉子結點並插入
2.插入完成後,判斷當前結點key的個數是否小於等於m-1,若滿足則結束,否則進行第3步
3.以結點中間的key為中心**成左右兩部分,然後將這個中間的key插入到父結點中,這個key的左子樹指向**後的左半部分,這個key的右子支指向**後的右半部分,然後將當前結點設定成父結點,繼續進行第2步
其實看完了插入操作,b樹的很多特性就都得到了解釋,尤其是特性2和特性5,就顯得一目了然了。我這麼說吧,除了根節點和葉子結點之外,其他節點最差也是**後的一半,所以特性2就很好理解。另外,你難道不覺得,b樹的增長方向,其實是向上增長麼???這麼一來,特性5也就非常好理解。
1.如果當前需要刪除的key位於非葉子結點上,則用後繼key(這裡的後繼key均指後繼記錄的意思)覆蓋要刪除的key,然後在後繼key所在的子支中刪除該後繼key。此時後繼key一定位於葉子結點上,這個過程和二叉搜尋樹刪除結點的方式類似。刪除這個記錄後執行第2步
2.該結點key個數大於等於math.ceil(m/2)-1,結束刪除操作,否則執行第3步
3.如果兄弟結點key個數大於math.ceil(m/2)-1,則父結點中的key下移到該結點,兄弟結點中的乙個key上移,刪除操作結束。否則,將父結點中的key下移與當前結點及它的兄弟結點中的key合併,形成乙個新的結點。原父結點中的key的兩個孩子指標就變成了乙個孩子指標,指向這個新結點。然後當前結點的指標指向父結點,重複上第2步
其實理解了插入操作後再回過頭來看看刪除操作,就顯得格外簡單,就是個逆操作。
查詢操作就十分的簡單了,我就不寫了,仿照二叉樹的遍歷方式就可以了,大差不差的就行。
上**!(只提供部分好吧,剩下的各位小夥伴加油!)
//0.一些基本的巨集定義
#define m 3
//1.定義b樹節點結構
struct btreenode
;
解釋一下為啥要這麼搞。首先b樹里插入的是keyvalue這樣的鍵值對,因此用list存放這些pair。因為經常會有插入刪除操作,所以我使用者list。childs是指向孩子節點的list。最後還有乙個parent方便咱們實現演算法。
然後是btree結構:
class
btree
;
還需要定義一些基本的操作:
private
://是否是葉子結點
bool
isleafnode
(struct btreenode* node)
;//節點的最大key
intmaxkey
(struct btreenode* node)
;//節點的最小key
intminkey
(struct btreenode* node)
;//將node**成兩個node,同時返回新的node指標和一組key_value
std::pair<
int,
void
*>
splitnode
(struct btreenode* node,
struct btreenode* newnode)
;//將一組key_value插入到node中,同時更新插入後的node的childs
void
upinsertnode
(struct btreenode* node, std::pair<
int,
void
*> kv,
struct btreenode* splitchildnode,
struct btreenode* newchildnode)
;
其中**操作需要注意的是,需要根據中間值將keyvalue鍊錶和childs鍊錶都做一分為二。
其中向上插入操作需要注意的是,跟新keyvalue鍊錶很簡單,而更新childs鍊錶時,一定是某乙個child後面新增乙個新的newsplitnode。
刪除操作與之類似,過分簡單,就不寫了。
在一些檔案系統或者資料庫的索引中,一般使用的是樹形結構(如b樹和b+樹),為啥不用hash或者二叉樹呢?
首先,hash的話好的情況下是o(1)的複雜度很棒,但是雜湊衝突是難免的,資料量上去以後會有很大的雜湊衝突的。
而二叉樹的搜尋時間複雜度確實要好於等於b樹,可是咱們還得考慮磁碟操作。畢竟檔案系統或者資料索引,其內容最後都是放在磁碟上的,而操作一次磁碟消耗的時間可就非常大了(至少比記憶體大很多),同樣的資料量下,二叉樹的高度(可能的切換磁軌次數)要比b樹多,這就是為啥用b樹而不用二叉樹的原因。
b+樹是在b樹的基礎上發展來的,對b樹做了進一步的優化。b+樹中的節點分為內部節點和葉子結點,其中內部節點不儲存資料value,只儲存key用作索引,b+樹的所有資料都在葉子結點上,且每個葉子節點都用鍊錶鏈結在一起的。
相較於b樹,b+樹的優點在於:
1.內部節點因為不存放value,因此如果把所有同一內部節點的關鍵字放在同一塊磁碟中,盤塊所能容納的關鍵字數量也就越多,一次性讀入記憶體中的需要查詢的關鍵字也就越多,相對io讀寫次數降低;
2.穩定。b樹咱們不清楚確定的訪問磁碟次數,而b+樹是確定的,因為資料一定在葉子結點上。
3.對於一些範圍搜尋,b+樹更為強大,因為b+樹的所有葉子結點都用鍊錶串聯在一起。如果我想找12~20之間的key值對應的value,那麼b樹要用中序遍歷,很麻煩,而b+樹只需要找到12,然後下面就是鍊錶操作了,非常方便。
與b樹有一些區別,當溢位時,中間值仍可保留在葉子結點中。
其實b樹就是b+樹的再一次改版。它將內部節點之間同層的也用鍊錶串聯在一起了。
這樣有什麼好處呢?就是當節點做插入操作時,如果發現此節點要溢位,則:
1.b+樹求助它的父親節點
2.b樹先求助它的兄弟節點,看看它的兄弟能否幫助其分擔一下,如果可以,那就很棒;如果不可以,則將自己和兄弟,每個人各拿出三分之一,構成乙個新的節點,然後再去父節點中增加新的指標。
b*樹這樣做的好處是,提高了節點的最低利用率,由b+樹的二分之一,提高到了三分之二(因為只要是產生節點,就一定有三分之一+三分之一的節點數量)
B樹B 樹B 樹和B 樹
原文link b樹 即二叉搜尋樹 1.所有非葉子結點至多擁有兩個兒子 left和right 2.所有結點儲存乙個關鍵字 3.非葉子結點的左指標指向小於其關鍵字的子樹,右指標指向大於其關鍵字的子樹 如 b樹的搜尋,從根結點開始,如果查詢的關鍵字與結點的關鍵字相等,那麼就命中 否則,如果查詢關鍵字比結點...
B 樹 B 樹 B 樹和B 樹
b樹 即二叉搜尋樹 1.所有非葉子結點至多擁有兩個兒子 left 和right 2.所有結點儲存乙個關鍵字 3.非葉子結點的左指標指向小於其關鍵字的子樹,右指標指向大於其關鍵字的子樹 如 b樹的搜尋,從根結點開始,如果查詢的關鍵字與結點的關鍵字相等,那麼就命中 否則,如果查詢關鍵字比結點關鍵字小,就...
B樹 B 樹和B 樹
一 b樹的查詢是在內部節點進行的,節點處存放了節點的所有資訊,即相當於存放的是乙個node。二 b 樹的查詢最終會在外部節點,或者稱為葉子節點,而內部節點不存放node,只存放node的索引,最終能夠在葉子節點處找到乙個指向該node的指標,從而結束查詢。b 樹的另乙個特點是在葉子節點中存放的所有n...