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;
}
图可能有点丑,多多包涵。