浅析B-树
B-树2016-11-12
#pragma once
#include<utility>
using namespace std;
template<typename K,int M>
struct BTreeNode
{
K _keys[M]; //M是孩子的个数,keys是关键值数组,多开辟一个便于处理
BTreeNode<K, M> *_subs[M + 1]; //孩子指针数组
BTreeNode<K, M> *_parent; //指向父亲的指针
size_t _size; //记录关键值的个数
BTreeNode()
:_parent(NULL)
, _size(0)
{
for (int i = 0; i < M; i++)
{
_keys[i] = K();
_subs[i] = NULL;
}
_subs[M] = NULL;
}
};
template<typename K,int M>
class BTree
{
typedef BTreeNode<K, M> Node;
public:
BTree()
:_root(NULL)
{}
pair<Node*,int> Find(const K& key)
{
Node* cur = _root;
Node* parent = NULL;
while (cur)
{
int i = 0;
while (i<(int)cur->_size)
{
if (cur->_keys[i] < key)
{
i++;
}
else if (cur->_keys[i]>key)
{
break;
}
else
{
return pair<Node*, int>(cur,i); //已经存在了
}
}
parent = cur;
cur = cur->_subs[i];
}
return pair<Node*, int>(parent, -1); //没找到,返回-1
}
bool Insert(const K& key)
{
if (_root==NULL) //如果是空树的话
{
_root = new Node;
_root->_keys[0] = key;
_root->_size++;
_root->_parent = NULL;
}
//如果不是空树的话,先找这个key存在还是不存在
pair<Node*, int> product= Find(key);
if (product.second != -1) //表示这个key已经存在了
{
return false; //已经存在,插入失败
}
Node* cur = product.first;
Node* sub = NULL;
K newKey = key;
while (1)
{
_InsertKey(cur, newKey, sub); //将关键值插入
if (cur->_size == M) //如果key已经大于M-1,则需要分裂
{
Node* tmp = new Node; //创建一个分裂后的结点
int mid = cur->_size / 2; //找到keys数组的中间位置的下标
int j = 0;
int i = mid+1; //从中间位置的下一个位置开始复制
for (; i <(int)cur->_size; ++i, ++j)
{
tmp->_keys[j] = cur->_keys[i]; //将关键字复制到tmp中
tmp->_subs[j] = cur->_subs[i]; //将孩子也复制到tmp中
if (tmp->_subs[j]) //如果孩子不为空,则让它指向分裂的结点
tmp->_subs[j]->_parent= tmp;
cur->_keys[i] = K();
cur->_subs[i] = NULL;
tmp->_size++;
}
//将最后一个右孩子也复制过来
tmp->_subs[j] = cur->_subs[i];
if (tmp->_subs[j])
tmp->_subs[j]->_parent = tmp;
cur->_subs[i] = NULL;
newKey = cur->_keys[mid];
cur->_keys[mid] = K();
sub = tmp;
cur->_size = mid;
if (cur == _root) //如果分裂的结点是根节点
{
_root = new Node;
_root->_keys[0] = newKey;
_root->_subs[0] = cur;
_root->_subs[1] = sub;
cur->_parent = _root;
sub->_parent = _root;
_root->_size = 1;
return true;
}
cur = cur->_parent;
}
else //不需要分裂,已经平衡
break;
}
return true;
}
bool Remove(const K& key)
{
if (_root == NULL) //树为空树,则删除失败
{
return false;
}
pair<Node*, int> product = Find(key);
if (product.second == -1) //如果key不再树中,则删除失败
{
return false;
}
Node* cur = product.first;
int pos = product.second; //记录要删除的位置
//将判断要删除的关键码所在结点是不是叶子结点,如果不是的话,要先将关键码交换到叶子结点中
if (cur->_subs[pos + 1] != NULL) //如果要删除的关键码的右子树不为空,则是非叶子结点的删除
{
Node* minkey = cur->_subs[pos + 1]; //用来记录要删除的关键码的右子树的最小的键值
while (minkey->_subs[0])
{
minkey = minkey->_subs[0];
}
//转换成删除这个最小的关键码
cur->_keys[pos] = minkey->_keys[0];
cur = minkey;
_MoveLeft(cur, 0); //把交换后的关键码删除掉
}
else
_MoveLeft(cur, pos);
//判断是否满足B树的条件,不满足的话就要调整
int mid =(M+1)/2-1; //求出关键码个数的下限,关键码最少为M/2-1,向上取整
while (1)
{
if ((int)cur->_size < mid) //关键码的个数小于上限值,则要进行调整
{
if (cur == _root)
break;
Node* parent = cur->_parent;
pos = 0;
while (parent->_subs[pos] != cur&&pos < (int)parent->_size)
pos++;
if (pos == 0) //进行左调整
_LeftAdjust(parent, cur, mid, pos);
else
_RightAdjust(parent, cur, mid, pos-1);
cur = parent;
}
else //如果不小于上限值的话,则表示已经满足B树的条件,则直接退出
break;
}
if (_root->_size == 0) //如果调整之后根的关键码个数已经减为0,则要把当前根删除掉,把他的孩子当做根
{
Node* del = _root; //记录要删的这个根
_root = _root->_subs[0];
if (_root)
_root->_parent = NULL; //将新根的父亲置空
delete del;
}
return true;
}
void InOder()
{
_InOder(_root);
}
~BTree()
{
_Destory(_root);
}
protected:
void _LeftAdjust(Node* parent, Node* cur, int mid, int pos) //当前结点cur在他父亲的左边,cur与他的右兄弟进行调整
{
//如果cur的右兄弟的关键码的个数已经大于关键码的上限,则就通过收养解决
Node* right = parent->_subs[pos+1]; //right指向cur的左兄弟
if ((int)right->_size>mid) //大于关键码的上限,通过收养解决
{
cur->_size++;
cur->_keys[cur->_size - 1] = parent->_keys[pos]; //把父节点的相应关键码下移
parent->_keys[pos] = right->_keys[0]; //把右兄弟的最小关键码上移到父亲关键码位置
//把右兄弟的最左孩子移动到cur的最右孩子处
cur->_subs[cur->_size] = right->_subs[0];
if (right->_subs[0])
{
right->_subs[0]->_parent = cur; //让这个孩子的父亲指向cur
}
right->_subs[0] = right->_subs[1];
_MoveLeft(right, 0); //把右兄弟中剩余的关键码向左移动
}
else //只能通过合并解决
_Merge(parent,cur,right,pos);
}
void _RightAdjust(Node* parent, Node* cur,int mid, int pos) //当前结点cur在他父亲的右边,cur与他的左兄弟进行调整
{
Node* left = parent->_subs[pos]; //left指向cur的左兄弟
if ((int)left->_size>mid) //左兄弟的关键码个数大于关键码上限的值
{
//cur先把最左边的位置空出来,再把父亲结点的相应关键码下移
_MoveRight(cur,0);
cur->_keys[0] = parent->_keys[pos]; //父亲相应关键码下移
parent->_keys[pos] = left->_keys[left->_size-1]; //将左兄弟中的最大关键码上移到父节点位置
cur->_subs[0] = left->_subs[left->_size]; //将左兄弟中的最后一个孩子移到cur的左边
if (left->_subs[left->_size])
{
left->_subs[left->_size]->_parent = cur; //将这个孩子结点的父亲指向cur
}
left->_keys[left->_size - 1] = K();
left->_subs[left->_size] = NULL;
left->_size--;
}
else
_Merge(parent,left,cur,pos);
}
void _Merge(Node* parent,Node* cur,Node* brother,int pos) //保留cur,合并兄弟结点
{
int i = cur->_size; //要插入的位置
cur->_keys[i] = parent->_keys[pos]; //先把父亲结点相应的关键码的值移动到左孩子中
cur->_subs[i + 1] = brother->_subs[0]; //把右兄弟的最左孩子移动过来
if (brother->_subs[0])
brother->_subs[0]->_parent = cur;
i++;
cur->_size++;
for (int j = 0; j < (int)brother->_size; ++i, ++j) //将兄弟结点指针拷贝过来
{
cur->_keys[i] = brother->_keys[j];
cur->_subs[i + 1] = brother->_subs[j + 1];
if (brother->_subs[j+1])
brother->_subs[j+1]->_parent = cur;
cur->_size++;
}
if (parent->_subs[pos] == brother)
parent->_subs[pos] = NULL;
else
parent->_subs[pos + 1] = NULL;
_MoveLeft(parent,pos);
delete brother;
}
void _Destory(Node* cur)
{
if (cur == NULL)
return;
int i = 0;
for (i = 0; i < (int)cur->_size; i++)
{
_Destory(cur->_subs[i]);
delete cur->_subs[i];
}
_Destory(cur->_subs[i]); //遍历最右边的孩子
delete cur->_subs[i];
}
void _InOder(Node* cur)
{
if (cur == NULL)
return;
int i = 0;
for (i = 0; i < (int)cur->_size; i++)
{
_InOder(cur->_subs[i]);
cout << cur->_keys[i] << " ";
}
_InOder(cur->_subs[i]); //遍历最右边的孩子
}
void _InsertKey(Node* cur,const K& key,Node* sub)
{
int i = cur->_size-1;
while (i >= 0) //将key插入到B树中
{
if (key < cur->_keys[i])
{
cur->_keys[i + 1] = cur->_keys[i];
cur->_subs[i + 2] = cur->_subs[i+1];
--i;
}
else //key比当前关键值小,则进行插入
break;
}
//key是这组keys中最小的,放在第0个位置
cur->_keys[i+1] = key;
cur->_subs[i+2] = sub;
if (sub != NULL) //孩子不为空
sub->_parent = cur; //孩子的parent指向cur
cur->_size++;
}
void _MoveLeft(Node* cur,int pos) //在叶子结点中删除一个关键码
{
if (cur == NULL)
return;
int i = 0;
for (i = pos; i < (int)cur->_size; ++i)
{
cur->_keys[i]=cur->_keys[i+1]; //将要删除的关键码覆盖掉
cur->_subs[i+ 1] = cur->_subs[i+ 2];
}
cur->_size--;
}
void _MoveRight(Node* cur,int pos)
{
int i = cur->_size-1; //当前结点最右关键码下标
for (; i >=pos; --i)
{
cur->_keys[i+1] = cur->_keys[i];
cur->_subs[i+2] = cur->_subs[i+1];
}
cur->_subs[1] = cur->_subs[0];
cur->_size++;
}
private:
Node* _root;
};