B+树性质
B+树
B+树的实现有多种,有关键字和子树个数相等的,还有向B树一样的子树比关键字数量多一个。
这里使用第二种方式。
1.树的每个节点最多有M个子树
2.根节点至少有两个子树(如果不是叶子节点)
3.除根节点之外的所有非叶子节点,至少有M/2个子树(向上取整)
4.所有非叶子节点包括keycount,child[0],key[1],child[1],key[2]..key[keycount],child[keycount]
5.节点包括内部节点和叶子节点,B+树的节点信息都保存在叶子节点中,内部节点不保存数据
6.每个叶子节点都保存着它相邻兄弟节点的指针。
keycount为该节点包含的关键字数量
key为关键字
child为子树,子树插在各个关键字之间,所以比key多一个
B+树的插入删除与B树十分相似,只是在操作时要注意对叶子链表的next和头指针进行调整,另外,在插入删除中的分裂、合并过程中,对于叶子节点与非叶子节点的处理稍有不同。
插入删除操作与B树基本一致:
https://blog.csdn.net/yunshuipiao123/article/details/88548622
下面直接贴出代码,如有错误之处,希望大家不吝赐教~
#ifndef _BPLUS_TREE_H
#define _BPLUS_TREE_H
#include<iostream>
#include<string>
#include<queue>
#include<algorithm>
using namespace std;
/*
B+树
B+树的实现有多种,有关键字和子树个数相等的,还有向B树一样的子树比关键字数量多一个。
这里使用第二种方式。
1.树的每个节点最多有M个子树
2.根节点至少有两个子树(如果不是叶子节点)
3.除根节点之外的所有非叶子节点,至少有M/2个子树(向上取整)
4.所有非叶子节点包括keycount,child[0],key[1],child[1],key[2]..key[keycount],child[keycount]
5.节点包括内部节点和叶子节点,B+树的节点信息都保存在叶子节点中,内部节点不保存数据
6.每个叶子节点都保存着它相邻兄弟节点的指针。
keycount为该节点包含的关键字数量
key为关键字
child为子树,子树插在各个关键字之间,所以比key多一个
*/
class MyBPlusTree
{
private:
enum {
M = 5,KEY_START = 1,KEY_MIN = (M + 1) / 2 - 1,
KEY_MAX = M - 1,CHILD_MIN = KEY_MIN + 1,CHILD_MAX = KEY_MAX + 1
};
typedef struct Node
{
bool leaf; //是否为叶子节点(其实可以不需要这个字段的,可以通过对子树第0个位置是否为空判断叶子节点)
int keys[M + 1]; //key下标1..M
Node* childs[M + 1]; //子树0..M
int keycount;
Node* parent;
Node* next;
Node(bool l = true,Node* p = nullptr, Node* n = nullptr )
:leaf(l),parent(p),next(n),keycount(0){}
}BPlusTree;
typedef struct Result
{
Node* node;
int keypos;
Result(Node* n = nullptr,int k = 1):node(n),keypos(k){}
}SearchInsertResult,SearchEraseResult;
BPlusTree* root; //根节点
BPlusTree* start; //链式叶子节点的头结点
bool release;
public:
MyBPlusTree():root(0),start(0),release(false){}
~MyBPlusTree(){
this->destroy();
}
private:
int find_keypos(int key,Node* node);
bool find_insert(int key,SearchInsertResult* res);
Node* find_last_node(Node* node);
void _insert(int key,int keypos,Node* dst,Node* child);
void split(Node* node,Node*& child);
void _destroy(Node* root);
bool find_delete(int key,SearchEraseResult* res);
int get_childpos(Node* parent,Node* child);
Node* combine(Node* node,Node* brother,Node* parent,int parentKeyPos);
public:
bool insert(int key);
bool erase(int key);
bool destroy();
bool layer_print();
bool leaf_print();
};
int MyBPlusTree::find_keypos(int key,Node* node)
{
int pos = KEY_START;
for(;pos <= node->keycount && node->keys[pos] < key; pos++);
return pos;
}
bool MyBPlusTree::find_insert(int key,SearchInsertResult* res)
{
if(root == nullptr)
return false;
auto ptmp = root;
int pos = 0;
while(!ptmp->leaf)
{
pos = this->find_keypos(key,ptmp);
ptmp = ptmp->childs[pos - 1];
}
pos = this->find_keypos(key,ptmp); //在叶子中找到合适位置
if(res){
res->node = ptmp;
res->keypos = pos;
}
return true;
}
void MyBPlusTree::_insert(int key,int keypos,Node* dst,Node* child)
{
for(int i = dst->keycount + 1; i >= keypos; i--)
{
dst->keys[i] = dst->keys[i - 1];
dst->childs[i] = dst->childs[i - 1];
}
dst->keys[keypos] = key;
dst->childs[keypos - 1] = child;
if(child)
child->parent = dst;
++dst->keycount;
}
void MyBPlusTree::split(Node* node,Node*& child)
{
int pos = (M + 1) / 2;
Node* left = new Node(node->leaf,node->parent);
left->childs[0] = node->childs[0];
if(!left->leaf)
left->childs[0]->parent = left;
for(int i = KEY_START; i < (left->leaf ? pos + 1:pos); i++){
left->keys[i] = node->keys[i];
left->childs[i] = node->childs[i];
if(!left->leaf){
left->childs[i]->parent = left;
}
++left->keycount;
}
node->childs[0] = node->childs[pos];
for(int i = pos + 1; i <= node->keycount; i++){
node->keys[i - pos] = node->keys[i];
node->childs[i - pos] = node->childs[i];
}
node->keycount = node->keycount - left->keycount - (left->leaf ? 0 : 1);
child = left;
}
MyBPlusTree::Node* MyBPlusTree::find_last_node(Node* node)
{
if(start == node)
return start;
auto ptmp = start;
while(ptmp)
{
if(ptmp->next == node)
break;
ptmp = ptmp->next;
}
return ptmp;
}
bool MyBPlusTree::insert(int key)
{
SearchInsertResult res;
if(this->find_insert(key,&res) == false){
root = new Node;
root->keys[KEY_START] = key;
root->keycount = 1;
start = root;
return true;
}
auto node = res.node;
auto keypos = res.keypos;
Node* child = nullptr;
auto mid = key;
bool finished = false;
while(node)
{
this->_insert(mid,keypos,node,child);
if(node->keycount > KEY_MAX)
{
mid = node->keys[(M + 1) / 2];
this->split(node,child);
if(node->parent == nullptr)
{
root = new Node(false);
root->keys[KEY_START] = mid;
root->childs[0] = child;
root->childs[1] = node;
root->keycount = 1;
child->parent = root;
node->parent = root;
finished = true;
if(child->leaf)
{
start = child;
child->next = node;
}
}else{
if(node && node->leaf){
auto last = this->find_last_node(node);
if(last == node){
start = child;
child->next = node;
}else{
last->next = child;
child->next = node;
}
}
node = node->parent;
keypos = this->find_keypos(key,node);
}
if(finished)
break;
}else
break;
}
return true;
}
bool MyBPlusTree::find_delete(int key,SearchEraseResult* res)
{
if(root == nullptr)
return false;
auto ptmp = root;
int pos = 0;
while(!ptmp->leaf)
{
pos = this->find_keypos(key,ptmp);
ptmp = ptmp->childs[pos - 1];
}
pos = this->find_keypos(key,ptmp);
if(ptmp->keys[pos] == key)
{
res->node = ptmp;
res->keypos = pos;
}else
return false;
return true;
}
int MyBPlusTree::get_childpos(Node* parent,Node* child)
{
int pos = -1;
for(int i = 0; i <= parent->keycount; i++){
if(parent->childs[i] == child){
pos = i;
break;
}
}
return pos;
}
MyBPlusTree::Node* MyBPlusTree::combine(Node* node,Node* brother,Node* parent,int parentKeyPos)
{
Node* mergedNode = new Node(node->leaf,node->parent);
int key = parent->keys[parentKeyPos];
for(int i = parentKeyPos; i < parent->keycount; i++){
parent->keys[i] = parent->keys[i + 1];
parent->childs[i] = parent->childs[i + 1];
}
--parent->keycount;
if(!mergedNode->leaf){
if(node->keycount == 0 || key >= node->keys[node->keycount]){
//说明key值的排序是node key brother也就是node在brother左侧
node->keys[node->keycount + 1] = key;
node->childs[node->keycount + 1] = brother->childs[0]; //注意子树也要移动
++node->keycount;
}else{
brother->keys[brother->keycount + 1] = key;
brother->childs[brother->keycount + 1] = node->childs[0];
++brother->keycount;
}
}
if(node->keys[1] > brother->keys[brother->keycount]){
mergedNode->childs[0] = brother->childs[0];
for(int i = 1; i <= brother->keycount; i++){
mergedNode->keys[i] = brother->keys[i];
mergedNode->childs[i] = brother->childs[i];
if(!mergedNode->leaf) //不要忘反指
mergedNode->childs[i]->parent = mergedNode;
}
for(int i = brother->keycount + 1; i <= brother->keycount + node->keycount; i++){
mergedNode->keys[i] = node->keys[i - brother->keycount];
mergedNode->childs[i] = node->childs[i - brother->keycount];
if(!mergedNode->leaf) //不要忘反指
mergedNode->childs[i]->parent = mergedNode;
}
if(mergedNode->leaf)
{
auto last = this->find_last_node(brother);
if(last == brother){
start = mergedNode;
}else{
last->next = mergedNode;
}
mergedNode->next = node->next;
}
}else{
mergedNode->childs[0] = node->childs[0];
for(int i = 1; i <= node->keycount; i++){
mergedNode->keys[i] = node->keys[i];
mergedNode->childs[i] = node->childs[i];
if(!mergedNode->leaf) //不要忘反指
mergedNode->childs[i]->parent = mergedNode;
}
for(int i = node->keycount + 1; i <= node->keycount + brother->keycount; i++){
mergedNode->keys[i] = brother->keys[i - node->keycount];
mergedNode->childs[i] = brother->childs[i - node->keycount];
if(!mergedNode->leaf) //不要忘反指
mergedNode->childs[i]->parent = mergedNode;
}
if(mergedNode->leaf)
{
auto last = this->find_last_node(node);
if(last == node){
start = mergedNode;
}else{
last->next = mergedNode;
}
mergedNode->next = brother->next;
}
}
mergedNode->keycount = node->keycount + brother->keycount;
parent->childs[parentKeyPos - 1] = mergedNode;
delete node;
delete brother;
return mergedNode;
}
bool MyBPlusTree::erase(int key)
{
SearchEraseResult res;
if(this->find_delete(key,&res) == false)
return false;
auto node = res.node;
int keypos = res.keypos;
//找到的节点都是叶子节点,直接移动删除
for(int i = keypos; i < node->keycount; i++)
{
node->keys[i] = node->keys[i + 1];
}
--node->keycount;
while(1)
{
if(node->parent == nullptr)
{
if(node->keycount == 0)
{
if(node->leaf){
root = nullptr;
}else{
root = node->childs[0];
}
auto last = this->find_last_node(node);
if(last == node){
start = nullptr;
}else{
last->next = node->next;
}
delete node;
}
return true;
}
if(node->keycount < KEY_MIN)
{
auto parent = node->parent;
int childpos = this->get_childpos(parent,node);
if(childpos > 0 && parent->childs[childpos - 1]->keycount > KEY_MIN)
{//说明左兄弟节点有,多个key值可以向他借
auto left = parent->childs[childpos - 1];
for(int i = node->keycount + 1; i >= 1; i--){
node->keys[i] = node->keys[i - 1];
node->childs[i] = node->childs[i - 1];
}
//------------------与B树操作不同------------
if(node->leaf) //这里与B树不同,B+树对叶子节点和非叶子节点的删除有一点差别
node->keys[1] = left->keys[left->keycount];
else
node->keys[1] = parent->keys[childpos];
++node->keycount;
//------------------------------------------
node->childs[0] = left->childs[left->keycount]; //携带的child
if(!node->leaf)
node->childs[0]->parent = node;
parent->keys[childpos] = left->keys[left->keycount - 1];
--left->keycount;
return true;
}else if(childpos < parent->keycount && parent->childs[childpos + 1]->keycount > KEY_MIN)
{//左兄弟借不到,右兄弟有多的,向他借
auto right = parent->childs[childpos + 1];
//------------------与B树操作不同------------
if(node->leaf)
node->keys[node->keycount + 1] = right->keys[KEY_START];
else
node->keys[node->keycount + 1] = parent->keys[childpos + 1];
++node->keycount;
//------------------------------------------
parent->keys[childpos + 1] = right->keys[KEY_START + 1];
node->childs[node->keycount] = right->childs[0];//携带的child
if(!node->leaf)
node->childs[0]->parent = node;
for(int i = 0; i < right->keycount; i++){
right->keys[i] = right->keys[i + 1];
right->childs[i] = right->childs[i + 1];
}
--right->keycount;
return true;
}else
{//借不到,最后一招,合并
Node* mergedNode = nullptr;
if(childpos > 0)
{//有左兄弟,与他和父节点中的一个key合并
auto left = parent->childs[childpos - 1];
mergedNode = this->combine(node,left,parent,childpos);
}else if(childpos < CHILD_MAX - 1)
{//右兄弟
auto right = parent->childs[childpos + 1];
mergedNode = this->combine(node,right,parent,childpos + 1);
}
node = mergedNode->parent; //上溯
}
}else
return true;
}
return true;
}
bool MyBPlusTree::layer_print()
{
if(root == nullptr)
return false;
Node* ptmp = root;
Node* node = nullptr;
queue<Node*> Q;
Q.push(ptmp);
while(!Q.empty())
{
node = Q.front();
Q.pop();
node->leaf ? 0 : Q.push(node->childs[0]);
for(int i = KEY_START; i <= node->keycount; i++){
cout<<node->keys[i]<<" ";
if(node->leaf == false)
Q.push(node->childs[i]);
}
}
cout<<endl;
return true;
}
void MyBPlusTree::_destroy(Node* root)
{
if(root == nullptr)
return;
if(root->leaf == false)
for(int i = root->keycount; i >= 0; i--)
this->_destroy(root->childs[i]);
delete root;
root = nullptr;
}
bool MyBPlusTree::destroy()
{
if(this->release == false)
{
this->_destroy(root);
this->release = true;
}
return true;
}
bool MyBPlusTree::leaf_print()
{
auto ptmp = start;
while(ptmp)
{
for(int i = KEY_START; i <= ptmp->keycount; i++)
cout<<ptmp->keys[i]<<" ";
ptmp = ptmp->next;
}
cout<<endl;
return true;
}
#endif // !_B+_TREE_H
B树与B+树的区别:
B树在每个节点中都保存了数据,而B+树只在叶子节点中保存了数据信息,非叶子节点只是被用于索引。
由于B+树的非叶子节点没有保存实际数据,所以在大小相同的节点中B+树能保存的键值更多。
由于B+树的保存数据的叶子节点都使用指针相连,所以在进行范围搜索时B+树的效率更高。
进行单个数据进行查找时,由于B+树的数据信息都保存在叶子中,所以每次都需要遍历都底,而B树则可能在根节点就找到了,因此在进行单个数据查询时,B树还是要更快一些。