1. 程式人生 > >平衡搜尋樹-BTree

平衡搜尋樹-BTree

B樹是一種適合外查詢的樹,是一種平衡 的多叉樹。
一棵M階(M>2)的B樹,是一棵平衡的M路平衡搜尋樹,可以是空樹或者滿足一下性質:
1。根節點至少有兩個孩子
2。每個非根節點有[M/2,M]個孩子
3。每個非根節點有[M/2-1,M-1]個關鍵字,並且以升序排列
4。每個節點孩子的數量比關鍵字的數量多一個。
5。 key[i]和key[i+1]之間的孩子節點的值介於key[i]、key[i+1]之間
6。 所有的葉子節點都在同一層
這裡寫圖片描述
這裡寫圖片描述
下面用程式碼來實現一下B樹

#pragma once

template<class K, class V, size_t M>
struct
BTreeNode { pair<K, V> _kvs[M]; // 多開一個空間,方便分裂 BTreeNode<K, V, M>* _subs[M+1]; BTreeNode<K, V, M>* _parent; size_t _size; // 關鍵字的數量 BTreeNode() :_parent(NULL) ,_size(0) { for (size_t i = 0; i < M+1; ++i) { _subs[i] = NULL; } } }; template
<class K, class V, size_t M> class BTree { typedef BTreeNode<K, V, 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
; while(i < cur->_size) { if (cur->_kvs[i].first > key) // 在[i]的左樹 break; else if (cur->_kvs[i].first < key) // 在後面 ++i; else return make_pair(cur, i); } parent = cur; cur = cur->_subs[i]; } return make_pair(parent, -1); } bool Insert(const pair<K, V>& kv) { if (_root == NULL) { _root = new Node; _root->_kvs[0] = kv; _root->_size = 1; return true; } pair<Node*, int> ret = Find(kv.first); if (ret.second >= 0) { return false; } Node* cur = ret.first; pair<K, V> newKV = kv; Node* sub = NULL; // 往cur插入newKV, sub while (1) { InsertKV(cur, newKV, sub); if (cur->_size < M) { return true; } else { // 分裂 Node* newNode = DivideNode(cur); pair<K, V> midKV = cur->_kvs[cur->_size/2]; cur->_size -= (newNode->_size+1); // 1.根節點分裂 if (cur == _root) { _root = new Node; _root->_kvs[0] = midKV; _root->_size = 1; _root->_subs[0] = cur; _root->_subs[1] = newNode; cur->_parent = _root; newNode->_parent = _root; return true; } else { sub = newNode; newKV = midKV; cur = cur->_parent; } } } } Node* DivideNode(Node* cur) { Node* newNode = new Node; int mid = cur->_size/2; size_t j = 0; size_t i = mid+1; for (; i < cur->_size; ++i) { newNode->_kvs[j] = cur->_kvs[i]; newNode->_subs[j] = cur->_subs[i]; if(newNode->_subs[j]) newNode->_subs[j]->_parent = newNode; newNode->_size++; j++; } newNode->_subs[j] = cur->_subs[i]; if(newNode->_subs[j]) newNode->_subs[j]->_parent = newNode; return newNode; } void InsertKV(Node* cur, const pair<K, V>& kv, Node* sub) { int end = cur->_size-1; while (end >= 0) { if (cur->_kvs[end].first > kv.first) { cur->_kvs[end+1] = cur->_kvs[end]; cur->_subs[end+2] = cur->_subs[end+1]; --end; } else { break; } } cur->_kvs[end+1] = kv; cur->_subs[end+2] = sub; if(sub) sub->_parent = cur; cur->_size++; } void InOrder() { _InOrder(_root); cout<<endl; } void _InOrder(Node* root) { if (root == NULL) return; Node* cur = root; size_t i = 0; for (; i < cur->_size; ++i) { _InOrder(cur->_subs[i]); cout<<cur->_kvs[i].first<<" "; } _InOrder(cur->_subs[i]); } private: Node* _root; }; void TestBTree() { BTree<int, int, 3> t; int a[] = {53, 75, 139, 49, 145, 36, 101}; for (size_t i = 0; i < sizeof(a)/sizeof(a[0]); ++i) { t.Insert(make_pair(a[i], i)); } t.InOrder(); } template<class K, size_t M> struct BPTreeNonLeafNode { K _keys[M]; void* _subs[M]; BPTreeNonLeafNode<K, M>* _parent; size_t _size; }; template<class K, class V, size_t M> struct BPTreeLeafNode { pair<K, V> kvs[M]; BPTreeLeafNode<K, V>* _next; BPTreeNonLeafNode<K, M>* _parent; size_t _size; }; template<class K, class V, size_t M> class BPTree { typedef BPTreeLeafNode<K, V, M> LeafNode; typedef BPTreeLeafNode<K, M> NonLeafNode; private: NonLeafNode* _root; LeafNode* _head; };

B+樹
B+樹是對B樹的一種變形樹,它與B樹的差異在於:
有k個子結點的結點必然有k個關鍵碼;
非葉結點僅具有索引作用,跟記錄有關的資訊均存放在葉結點中。
樹的所有葉結點構成一個有序連結串列,可以按照關鍵碼排序的次序遍歷全部記錄。
這裡寫圖片描述
B+樹更適合檔案索引系統
B*樹
這裡寫圖片描述
  B*樹定義了非葉子結點關鍵字個數至少為(2/3)*M,即塊的最低使用率為2/3
(代替B+樹的1/2);
       B+樹的分裂:當一個結點滿時,分配一個新的結點,並將原結點中1/2的資料
複製到新結點,最後在父結點中增加新結點的指標;B+樹的分裂隻影響原結點和父
結點,而不會影響兄弟結點,所以它不需要指向兄弟的指標;
       B*樹的分裂:當一個結點滿時,如果它的下一個兄弟結點未滿,那麼將一部分
資料移到兄弟結點中,再在原結點插入關鍵字,最後修改父結點中兄弟結點的關鍵字
(因為兄弟結點的關鍵字範圍改變了);如果兄弟也滿了,則在原結點與兄弟結點之
間增加新結點,並各複製1/3的資料到新結點,最後在父結點增加新結點的指標;
       所以,B*樹分配新結點的概率比B+樹要低,空間使用率更高;