平衡搜尋樹-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+樹要低,空間使用率更高;