B樹的基本定義與B樹的插入
阿新 • • 發佈:2018-12-25
B樹:
1.B樹的概念:
B樹是平衡的多叉樹,一個節點有多於兩個(不能小於)結點的平衡多叉樹。
缺點:浪費空間
2.B樹的基本性質:
<1>.根節點至少有兩個孩子
<2>.每個非根節點至少有M/2(上取整)個孩子,至多有M個孩子。
<3>.每個非根節點至少有M/2-1(上取整)個關鍵字,至多有M-1個關鍵字。並以升序排列。
<4>.key[i]和key[i+1]之間的孩子節點的值介於key[i]和key[i+1]之間。
<5>.所有的葉子節點都在同一層。
3.B樹的插入:
<4>.B樹的節點:
因為B樹中有關鍵字與孩子,如上圖所示因為其包含key[M-1]與sub[M]。則將其kv陣列給成pair型。即pair
template<class K,class V,size_t M>//M 為節點個數
struct BTreeNode
{
//kvs和subs多一個空間是為了插入以後在分裂
//簡化分裂的邏輯
pair<K, V>_kvs[M]; //kvs
BTreeNode<K, V, M>* _subs[M + 1];//孩子節點
BTreeNode<K, V, M>* _parent;
size_t _size;//kvs的數量
BTreeNode()
:_parent(NULL)
, _size(0 )
{
for (size_t i = 0; i < M+1; i++)
{
_subs[i] = NULL;
}
}
};
<6>. B樹的構造:
函式1:Find函式:
查詢樹中是否有和key值相同的數值。
查詢返回值是一個pair,如果找到了結點,返回結點cur和此結點的位置,沒有找到則返回父節點和-1。
查詢的具體過程就是遍歷結點構成的陣列即可,需要注意的是邊界條件,同時在遍歷過程中看在樹的左邊和後面。
pair<pNode, int>Find(const K&key)//查詢函式
{
pNode cur = _root;
pNode parent = NULL;
while (cur)
{
size_t i = 0;
while (i < cur->_size)
{
if (cur->_kvs[i].first == key)//若找到則返回節點與其位置
{
return make_pair(cur, i);
}
else if (cur->_kvs[i].first>key)//若key值小於cur->_kvs[i].first,則在其左子樹中
{
break;
}
else //在其右子樹中
{
++i;
}
}
parent = cur;
cur = cur->_subs[i];
}
//若找不到則返回-1;
return make_pair(parent, -1);
}
函式2:Insert函式:
插入方法:
①如果root為空,則構造結點直接插入,將size值更新為1。
②當root不為空時,呼叫Find函式。由於返回值是一個pair,所以判斷返回值的第二個引數,如果存在,則引數為大於等於0,插入不成功返回false,否則進行插入
④沒有找到的時候返回pair的第一個引數是插入節點的父親結點,構造節點呼叫insertkv(此處邏輯複雜,單獨寫出來)進行插入。
⑤判斷插入後結點的size值,如果小於M則直接插入成功,反之需要進行分裂。
size_t j = 0;//將右半區間拷走
for (size_t i = mid + 1; i < cur->_size; ++i)
{
brother->_kvs[j] = cur->_kvs[i];
brother->_subs[j] = cur->_subs[i];
if (brother->_subs[j])
{
brother->_subs[j]->_parent = brother;
}
++j;
brother->_size++;//增加節點
}
//拷完後還剩一個右孩子
brother->_subs[j] = cur->_subs[cur->_size];//將最後一個拷給brother。
cur->_size = cur->_size - brother->_size -1;
完整的插入程式碼如下:
#include <iostream>
#include <string>
using namespace std;
template<class K,class V,size_t M>//M 為節點個數
struct BTreeNode
{
//kvs和subs多一個空間是為了插入以後在分裂
//簡化分裂的邏輯
pair<K, V>_kvs[M]; //kvs
BTreeNode<K, V, M>* _subs[M + 1];//孩子節點
BTreeNode<K, V, M>* _parent;
size_t _size;//kvs的數量
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>//M 為節點個數
class BTree
{
typedef BTreeNode<K, V, M> Node;
typedef Node* pNode;
public:
BTree()
:_root(NULL)
{}
pair<pNode, int>Find(const K&key)//查詢函式
{
pNode cur = _root;
pNode parent = NULL;
while (cur)
{
size_t i = 0;
while (i < cur->_size)
{
if (cur->_kvs[i].first == key)//若找到則返回節點與其位置
{
return make_pair(cur, i);
}
else if (cur->_kvs[i].first>key)//若key值小於cur->_kvs[i].first,則在其左子樹中
{
break;
}
else //在其右子樹中
{
++i;
}
}
parent = cur;
cur = cur->_subs[i];
}
//若找不到則返回-1;
return make_pair(parent, -1);
}
bool Insert(const pair<K, V>&kv)//插入函式
{
//1.若為空樹
if (_root == NULL)
{
_root = new Node;
_root->_kvs[0] = kv;
_root->_size = 1;
return true;
}
//2.
pair<pNode, int>ret = Find(kv.first);
if (ret.second != -1)
{
return false;//說明key在節點中`
}
pNode cur = ret.first;
pair<K, V>newkv = kv;
pNode sub = NULL;
while (1)//不斷地向上分裂
{
Insertkv(cur, newkv, sub);
if (cur->_size < M)
{
return true;
}
else
{
//走到此,表示節點已滿,需要進行分裂
pNode brother = new Node;
//將cur節點的右半區間分裂給兄弟節點
size_t mid = (M / 2);
size_t j = 0;//將右半區間拷走
for (size_t i = mid + 1; i < cur->_size; ++i)
{
brother->_kvs[j] = cur->_kvs[i];
brother->_subs[j] = cur->_subs[i];
if (brother->_subs[j])
{
brother->_subs[j]->_parent = brother;
}
++j;
brother->_size++;//增加節點
}
//拷完後還剩一個右孩子
brother->_subs[j] = cur->_subs[cur->_size];//將最後一個拷給brother。
cur->_size = cur->_size - brother->_size -1;
if (cur == _root)
{
_root = new Node;
_root->_kvs[0] = cur->_kvs[mid];
_root->_subs[0] = cur;
_root->_subs[1] = brother;
_root->_size = 1;
cur->_parent = _root;
brother->_parent = _root;
return true;
}
else//有父親時
{
newkv = cur->_kvs[mid];
sub = brother;
cur = cur->_parent;
}
}
}
}
void Insertkv(pNode cur, const pair<K, V>&kv, pNode sub)//sub為一個孩子
{
int end = cur->_size - 1;
while (end >= 0)
{
if (cur->_kvs[end].first < kv.first)//若其值大於cur->_kvs[end].first,則需放在end+1的位置
{
break;
}
else
{
cur->_kvs[end + 1] = cur->_kvs[end];
cur->_subs[end + 2] = cur->_subs[end + 1];
--end;
}
}
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)//cur->_size:節點的個數即為上邊key的個數
{
_InOrder(cur->_subs[i]);//左孩子
cout << cur->_kvs[i].first << " " << cur->_kvs[i].second << endl;
}
_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();
}
int main()
{
TestBTree();
system("pause");
return 0;
}
圖可能有點醜,多多包涵。