適用於外查詢的平衡樹——B樹
阿新 • • 發佈:2018-12-25
什麼是B樹:
1970年,R.Bayer和E.mccreight提出了一種適用於外查詢的樹,它是一種平衡的多叉樹,稱為B樹(或B-樹、B_樹)。在B-樹中查詢給定關鍵字的方法是,首先把根結點取來,在根結點所包含的關鍵字K1,…,kj查詢給定的關鍵字(可用順序查詢或二分查詢法),若找到等於給定值的關鍵字,則查詢成功;否則,一定可以確定要查的關鍵字在某個Ki或Ki+1之間,於是取Pi所指的結點繼續查詢,直到找到,或指標Pi為空時查詢失敗。
圖解B樹的插入和分裂:
下面是關於B樹的程式碼,以及涉及到的遍歷B樹的面試題
“test.cpp”
#define _CRT_SECURE_NO_WARNINGS 1 #include <iostream> using namespace std; #include "BTree.h" void Test1() { BTree<int,3> bt; bt.Insert(20); bt.Insert(30); bt.Insert(10); } void Teat2() { BTree<int,3> bt; int arr[] = {53,75,139,49,145,36,101}; int size = sizeof(arr)/sizeof(arr[0]); for (size_t i = 0;i < size;i++) { bt.Insert(arr[i]); } bt.InOrder(); } int main() { //Test1(); Teat2(); system("pause"); return 0; }
"BTree.h"
<strong><span style="font-size:18px;">#pragma once template<class K,int M> struct BTreeNode { K _key[M];//多給了一個位置是為了方便處理分裂 BTreeNode<K,M>* _subs[M+1];//多給了一個位置是為了方便處理分裂 BTreeNode<K,M>* _parent; size_t _size;//存了幾個關鍵字 //建構函式 BTreeNode() :_parent(NULL) ,_size(0) { for (size_t i = 0;i < M;i++) { _key[i] = K(); _subs[i] = NULL; } _subs[M] = NULL; } }; template<class K,int M> class BTree { typedef BTreeNode<K,M> Node; public: BTree()//建構函式 :_root(NULL) {} pair<Node*,int> Find(const K& key) { Node* parent = NULL; Node* cur = _root; while (cur) { size_t i = 0; for (i = 0;i < cur->_size;) { if (cur->_key[i] == key) { return pair<Node*,int>(cur,i); } else if (cur->_key[i] > key) { break; } else { i++; } } parent = cur; cur = cur->_subs[i]; } return pair<Node*,int>(parent,-1);//_root == NULL 的情況下返回 //找不到的情況下返回 } bool Insert(const K& key) { //第一個資料插入的情況 if (_root == NULL) { _root = new Node; _root->_key[0] = key; _root->_size = 1; return true; } pair<Node*,int> ret = Find(key); if (ret.second != -1) { //表明key值已經存在,則無需插入,錯誤返回 return false; } Node* cur = ret.first; K newkey = key; Node* sub = NULL; while (1) { InsertKey(cur,newkey,sub); if (cur->_size < M) { return true; } //分裂 int div = M / 2; Node* tmp = new Node;//先建立一個新的結點 int index = 0; size_t i = 0; for (i = div + 1;i <cur->_size;i++) { //拷貝右半區間的資料到新結點 tmp->_key[index++] = cur->_key[i]; tmp->_size++; cur->_key[i] = K(); } index = 0; for (i = div+1;i<cur->_size;i++) { //拷貝右半區間的孩子結點到新結點 tmp->_subs[index++] = cur->_subs[i]; if (cur->_subs[i]) { cur->_subs[i]->_parent = tmp; } } cur->_size = cur->_size - tmp->_size - 1;//中位數往上提 if (cur->_parent == NULL) { _root = new Node; _root->_key[0] = cur->_key[div]; cur->_key[div] = K(); _root->_subs[0] = cur; cur->_parent = _root; _root->_subs[1] = tmp; tmp->_parent = _root; _root->_size = 1; return true; } else { newkey = cur->_key[div]; cur->_key[div] = K(); cur = cur->_parent; sub = tmp; } } return true;//只要能跳出while(1)的死迴圈,就一定有返回值的了, //這裡的返回值是沒有意義的 } void InOrder() { _InOrder(_root); cout<<endl; } private: void _InOrder(Node* root) { if (root == NULL) { return; } size_t i = 0; for (i = 0;i < root->_size;i++) { _InOrder(root->_subs[i]); cout<<root->_key[i]<<" "; } _InOrder(root->_subs[i]); } void InsertKey(Node* cur,const K& key,Node* sub) { int end = cur->_size-1;//讓end指向資料的最後一個位置 while (end >= 0) { if (cur->_key[end] > key) { cur->_key[end+1] = cur->_key[end]; cur->_subs[end+2] = cur->_subs[end+1]; end--; } else { //end->_key < key break; } } cur->_key[end+1] = key; cur->_subs[end+2] = sub; if (sub) { //如果孩子存在 sub->_parent = cur; } cur->_size++; } protected: Node* _root; };</span></strong>