参考:邓俊辉老师《数据结构C++语言实现》
B-树节点由多个关键码组成,在数据结构中用向量表示,同理,关键码的孩子也存在向量中,结构如下:
key: key0 key1 key2 key3 key4 (存数值)
child: child0 child1 child2 child3 child4 child5 (存指针)
BNode以及BTree定义:
//
// Created by Jayson on 2023/8/5.
//
#include <iostream>
#include "vector"
using namespace std;
template<typename T>
class BTNode{
public:
BTNode<T>* parent;
vector<T> key;
vector<BTNode<T>*> child;
BTNode(){//构造含有0个关键码的节点
parent = NULL;
child.push_back(NULL);
}
BTNode(T e,BTNode<T>* lc = NULL, BTNode<T>*rc = NULL){//构造含有一个关键码的节点
parent = NULL;
key.push_back(e);
child.push_back(lc);
child.push_back(rc);
if(lc) lc->parent = this;
if(rc) rc->parent = this;
}
};
template<typename T>
class BTree{
public:
BTNode<T>* _root;
BTNode<T>* _hot;//搜索到节点的父亲,若搜索失败,则为"空节点"的父亲
int _order;//B-树的阶,也就是child向量能包含元素的最大个数
int _size;//关键码总数
void solveOverFlow(BTNode<T>* v);//解决因为插入操作而产生的上溢
void solveUnderFlow(BTNode<T>* v);//解决因为删除操作而产生的下溢
BTree(int ord){
_order = ord;
_size = 0;
_root = new BTNode<T>();
}
bool empty(){return !_root;}
//查、插、删
BTNode<T>* search(T e);
bool insert(T e);
bool remove(T e);
};
实现:
//辅助函数,在向量中查找,返回不大于该元素最大的秩,使用二分查找
template<typename T>
int binSearchInBTNode(vector<T> A,T e){
if(A.size() == 0){
return 0;
}
int lo = 0;
int hi = A.size();
while(lo<hi){
int mid = (lo + hi) / 2;
if(e < A[mid]){
hi = mid;
}else{
lo = mid + 1;
}
}
lo = lo - 1;
return lo;
}
template<typename T>
BTNode<T>* BTree<T>::search(T e) {
BTNode<T>* v = _root;
_hot = NULL;
while(v){
int r = binSearchInBTNode(v->key,e);
if( (r >= 0) && v->key[r] == e ) return v;
_hot = v;
v = v->child[r+1];
}
return NULL;
}
template<typename T>
bool BTree<T>::insert(T e) {
BTNode<T>* v = search(e);
if(v) return false;
//查找失败,必失败于外部节点,_hot此时指向叶节点
int r = binSearchInBTNode(_hot->key,e);
_hot->key.insert(_hot->key.begin()+r+1,e);
_hot->child.insert(_hot->child.begin()+r+2,NULL);
_size++;
solveOverFlow(_hot);
return true;
}
template<typename T>
void BTree<T>::solveOverFlow(BTNode<T> *v) {
if(v->child.size() <= _order) return;
int rank = v->key.size() / 2;
BTNode<T>* newNode = new BTNode<T>();
for(int i = rank+1; i < v->key.size(); i++){
newNode->key.push_back(v->key[i]);
}
newNode->child.clear();
for(int i =rank+1; i < v->child.size(); i++){
newNode->child.push_back(v->child[i]);
}
for(int i = 0; i < newNode->child.size(); i++){
if(newNode->child[i])
newNode->child[i]->parent = newNode;
}
T value = v->key[rank];//保存上溢节点值
v->key.resize(rank);
BTNode<T>* p = v->parent;
if(!p){//当前节点已经是根,溢出称为新根
_root = new BTNode<T>();
p = _root;
p->child[0] = v;
p->child[1] = newNode;
v->parent = p;
newNode->parent = p;
p->key.push_back(value);
}else{
int r = binSearchInBTNode(p->key,v->key[0]);//注意这里search的元素是key[0]
p->key.insert(p->key.begin()+r+1,value);
p->child.insert(p->child.begin()+r+2,newNode);
newNode->parent = p;
}
solveOverFlow(p);
}
template<typename T>
bool BTree<T>::remove(T e) {
BTNode<T>* v = search(e);
if(!v) return false;
int rank = binSearchInBTNode(v->key,e);
if(v->child[0]){
//v不是叶子节点
BTNode<T>* u = v->child[rank+1];
while(u->child[0]){
u = u->child[0];
}
v->key[rank] = u->key[0];
v = u;
rank = 0;
}
v->key.erase(v->key.begin()+rank);
v->child.erase(v->key.begin()+rank+1);
_size--;
solveUnderFlow(v);
}
template<typename T>
void BTree<T>::solveUnderFlow(BTNode<T> *v) {
if( v->child.size() >= ((_order+1)/2) )return;//递归基:阶数满足条件
BTNode<T>* p = v->parent;
if(!p){//递归基:根节点size为0
if(v->key.size()==0 && v->child[0]){
_root = v->child[0];
v->child[0]->parent = NULL;
v->child[0] = NULL;
}
return;
}
int r = 0;
while(p->child[r] != v){
r++;
}//确定v是r的第几个孩子
//case1
if(r>0){//左兄弟必然存在 此时p->child[r] == v;p->child[r-1] == 左兄弟
BTNode<T>* ls = p->child[r-1];
if(ls->child.size() >= ((_order+1)/2)+1){//左兄弟能借
v->key.insert(v->key.begin(),p->key[r-1]);
T value = ls->key[ls->key.size()-1];
p->key[r-1] = value;
v->child.insert(v->child.begin(),ls->child[ls->child.size()-1]);
if(ls->child[ls->child.size()-1]) v->child[0]->parent = v;
ls->key.resize(ls->key.size()-1);
ls->child.resize(ls->child.size()-1);
return;//修复完成
}
}
//case2
if(r<p->child.size()-1){//右兄弟必然存在
BTNode<T>* rs = p->child[r+1];
if(rs->child.size() >= ((_order+1)/2)+1)){//右兄弟能借
v->key.push_back(p->key[r-1]);
T value = rs->key[0];
p->key[r-1] = value;
v->child.push_back(rs->child[0]);
if(rs->child[0]) rs->child[0]->parent = v;
rs->key.erase(rs->key.begin());
rs->child.erase(rs->child.begin());
return;//修复完成
}
}
//case3左右兄弟都无法借关键码,因此需要合并
if(r>0){//与左兄弟合并
BTNode<T>* ls = p->child[r-1];
ls->key.push_back(p->key[r-1]);
p->key.erase(p->key.begin()+r-1);
p->child.erase(p->child.begin()+r);
for(int i = 0; i<v->key.size(); i++){
ls->key.push_back(v->key[i]);
}
for(int i = 0; i<v->child.size(); i++){
ls->child.push_back(v->child[i]);
if(v->child[i]) v->child[i]->parent = ls;
}
delete v;
}else{//与右兄弟合并
BTNode<T>* rs = p->child[r+1];
rs->key.insert(rs->key.begin(),p->key[r]);
p->child.erase(p->child.begin()+r);
p->key.erase(p->key.begin()+r);
for(int i = v->key.size()-1;i>=0;i--){
rs->key.insert(rs->key.begin(),v->key[i]);
}
for(int i = v->child.size()-1; i>=0;i--){
rs->child.insert(rs->child.begin(),v->child[i]);
if(v->child[i]) v->child[i]->parent = rs;
}
delete v;
}
solveUnderFlow(p);
return;
}
本文介绍了B-树的基本概念,展示了如何使用C++模板类实现B-树,包括节点结构、关键码处理、插入和删除操作,以及解决插入和删除操作导致的节点大小溢出问题。
&spm=1001.2101.3001.5002&articleId=132124898&d=1&t=3&u=73522b42e7e34c0db9ab87b102223bc3)
464

被折叠的 条评论
为什么被折叠?



