这里推荐一篇图文并茂的好文章,
B树、B+树、B*树
这里我实现代码过于粗糙,应该有很多地方可以改进,
另外我并没有实现b+树以及b*树,但其实应该也不复杂,无非是在struct增加指向兄弟结点的指针,应该也容易实现:
/*
* B 树
* */
#include "iostream"
#include "vector"
#include <cstring>
#include <typeinfo>
#include <cmath>
#include <algorithm>
using namespace std;
//#define DEGREE 6;
const int DEGREE = 4;
int maxD = DEGREE - 1;
int minD = (int)std::ceil(DEGREE / 2.) - 1;/* NOLINT */
template <class T>
int getArrayLen(T& array)
{//使用模板定义一 个函数getArrayLen,该函数将返回数组array的长度
return (sizeof(array) / sizeof(array[0]));
}
// TODO: 用预值,定义data数组
typedef struct BTnode {
// int key;
vector<int> data;
vector<BTnode*> children;
BTnode *father{};
}BTNode,*lpBNode;
void print(vector<int>& st){
for(const auto& i:st){
cout<<i<<" ";
}
cout<<endl;
}
int getPositonForF(lpBNode btn){
lpBNode pf = btn->father;
if(pf == nullptr)return 0;
else {
int i;
for(i=0;i<(int)pf->children.size();i++){
if(pf->children.at(i)==btn)
break;
}
return i;
}
}
void BtSplit(lpBNode& btr){
// 超过maxD,进行分裂
auto right = new BTNode;
// lpBNode right;
int len = btr->data.size(); // 除了根结点外,n个关键值,一定会有n+1个指针
// int clen = btr->children.size();
int rightStart = (int)std::ceil((float)len / 2.);//4,4
// 移除数据
for(int i=rightStart;i<len+1;i++){
if(i<len) {
right->data.push_back(btr->data.at(i));
}
// 把children 同时划分;
if(!btr->children.empty()){
auto child = btr->children.at(i);
if(child!=nullptr) {
child->father = right;
right->children.push_back(child);
}
}
}
int v = btr->data[rightStart - 1];
// 当前结点清理;以及清理多的子结点
for(int i=rightStart-1;i<len;i++){
btr->data.pop_back();
if(!btr->children.empty()) {
btr->children.pop_back();
}
}
lpBNode pf = btr->father;
int postion = getPositonForF(btr);
if(pf == nullptr){
auto node = new BTNode;
node->data.push_back(v);
node->children.push_back(btr);
btr->father = node;
node->children.push_back(right);
right->father = node;
} else {
right->father = pf;
pf->children.insert(pf->children.begin()+postion+1,right);
pf->data.push_back(v);
sort(pf->data.begin(),pf->data.end());
if( pf->data.size() <= maxD) return;
else BtSplit(pf);
}
}
void insert(lpBNode& btr, int value){
if(btr == nullptr){
auto node = new BTNode;
node->data.push_back(value);
btr = node;
return;
}
lpBNode bt = btr;
int index;
// 一直往下找到没有子结点的然后执行插入
while (!bt->children.empty()){
int curBtLen = bt->data.size();
for(index=0;index<=curBtLen;++index){
if(index!=curBtLen) {
if (value < bt->data[index])break;
} else break;
}
bt = bt->children.at(index);// 指针偏移
}
int btsize = bt->data.size();
int i;
for(i=0;i<=btsize;i++){
if(i==btsize)break;
if(value>bt->data[i])continue;
else break;
}
bt->data.insert(bt->data.begin()+i,value);
if( bt->data.size() <= maxD){
return;
} else{
BtSplit(bt); // 从这个点开始向上迭代分割
}
lpBNode top = bt;
while (bt->father!=nullptr){
bt = bt->father;
}
btr = bt;
}
lpBNode builtBtree(std::vector<int>& nodes){
lpBNode btn;
btn = nullptr;
for(auto node:nodes){
insert(btn,node);
}
return btn;
}
int maxHeight(lpBNode btn){
if(btn == nullptr){
return 0;
}
// 根据我观察到的b树的特点,所有子树高度是一样的
if(!btn->children.empty())return maxHeight(btn->children[0])+1;
else return 1;
}
// 找到结点
lpBNode find(lpBNode btn,int node){
if(btn == nullptr){
return nullptr;
}
for (int i=0;i<(int)btn->data.size();i++) {
if(btn->data.at(i) == node){
return btn;
}
}
if (!btn->children.empty()) {
for(auto & i : btn->children){
auto t = find(i,node);
if(t != nullptr) return t;
}
// 都没返回 就返回nullptr;
return nullptr;
}
else {
return nullptr;
}
}
lpBNode findPrecursor(lpBNode node,int v){
int pdsize = node->data.size();
int i;
for(i=0;i<=pdsize;i++){
if(i<pdsize) {
if (node->data[i] == v)break;
}
}
if(i==pdsize)return nullptr;
// 前驱就是i,后继就是i+1
if(node->children.empty())return node; // fixme: maybe fix up
else {
node = node->children[i];
while (!node->children.empty()){
node = *node->children.end();
}
return node;
}
}
void remove_fix(lpBNode& targetNode){
if(targetNode ->data.size() <= maxD && targetNode->data.size() >= minD){
return;
} else {
lpBNode ph = targetNode->father;
if(ph==nullptr)
return;
int pcsize = ph->children.size();// 子结点数量
int i;
for (i = 0; i < pcsize; i++) {
if (ph->children[i] == targetNode) break; // 一定会有1个
}
// 从左到右来找兄弟结点
// 左边 兄弟结点关键字足够借给该结点
if (i - 1 >= 0 && ph->children[i - 1]->data.size() - 1 >= minD) {
int dsize = ph->children[i - 1]->data.size();
int lv = ph->children[i - 1]->data.at(dsize - 1);
ph->children[i - 1]->data.pop_back();
int pv = ph->data.at(i - 1);
targetNode->data.insert(targetNode->data.begin(), pv);
ph->data.at(i - 1) = lv;
}
// 右边 兄弟结点关键字足够借给该结点
else if (i + 1 < pcsize && i + 1 < maxD && ph->children[i + 1]->data.size() - 1 >= minD) {
int dsize = ph->children[i + 1]->data.size();
int lv = ph->children[i + 1]->data.at(0);
ph->children[i + 1]->data.erase(
ph->children[i + 1]->data.begin(),
ph->children[i + 1]->data.begin() + 1);
int pv = ph->data.at(i);
targetNode->data.push_back(pv);
ph->data.at(i) = lv;
} else if (i - 1 >= 0 && ph->children[i - 1]->data.size() - 1 < minD) {
// 合并结点
ph->children[i - 1]->data.insert(ph->children[i - 1]->data.end(),
targetNode->data.begin(), targetNode->data.end());
ph->children[i-1]->children.insert(ph->children[i-1]->children.end(),
targetNode->children.begin(),
targetNode->children.end());// 把该结点孩子弄到合并点那里去
ph->children.erase(remove(ph->children.begin(), ph->children.end(), targetNode),
ph->children.end());// 删除这个结点
int pv = ph->data.at(i - 1);
ph->children[i - 1]->data.insert(ph->children[i - 1]->data.end(),
pv);
ph->data.erase(ph->data.begin() + i - 1);
remove_fix(ph);
} else if (i + 1 < pcsize && i + 1 <maxD && ph->children[i + 1]->data.size() - 1 < minD) {
// 右子结点合并该结点所有剩余结点
ph->children[i + 1]->data.insert(ph->children[i + 1]->data.begin(),
targetNode->data.begin(), targetNode->data.end());
ph->children[i+1]->children.insert(ph->children[i+1]->children.begin(),
targetNode->children.begin(),
targetNode->children.end());// 把该结点孩子弄到合并点那里去
ph->children.erase(remove(ph->children.begin(), ph->children.end(), targetNode),
ph->children.end());// 删除这个结点
int pv = ph->data.at(i);
// 右子节点拿父亲的值
ph->children[i]->data.insert(ph->children[i]->data.begin(),
pv);
// 父亲移除那个值
ph->data.erase(ph->data.begin() + i);
remove_fix(ph);
}
}
}
// 用的前驱方式
void remove(lpBNode& btn,int value){
// 先找到要删除的关键结点
lpBNode targetNode = find(btn, value);
if(targetNode == nullptr){
cout<<"cant't find target value"<<endl;
return;
}
if(!targetNode->children.empty()){ // 确保有子结点
// 前驱点一定是叶子结点
lpBNode precursor = findPrecursor(targetNode, value);
if(precursor==nullptr){
cout<<"current value not matched"<<endl;
return;
}
int i;
for (i=0;i<(int)targetNode->data.size();i++) {
if(targetNode->data.at(i) == value)break;
}
// auto lastNode = pre
int cvalue = *(precursor->data.end()-1);//前驱点最大值
targetNode->data.at(i) = cvalue;
precursor->data.erase(std::remove(precursor->data.begin(),
precursor->data.end(),cvalue),
precursor->data.end());
remove_fix(precursor);
//非叶子结点清理
}
else {// 叶子结点清理
targetNode->data.erase(std::remove(targetNode->data.begin(), // 删除固定值
targetNode->data.end(), value),
targetNode->data.end());
remove_fix(targetNode);
}
// 要找到前驱去代替那个值
while (btn->father!=nullptr){
btn = btn->father;
}
}
void show(lpBNode btn){
if (btn == nullptr){
std::cout<<"B tree is null"<<std::endl;
return;
}
int i = 0;
int height = maxHeight(btn);
std::vector<int> indents;
std::vector<lpBNode > printContainer = std::vector<lpBNode>{btn};
std::vector<lpBNode > storeContainer;
std::cout<<"maxHeight: "<<height<<std::endl;
for(i=height;i>0;i--){
int indent = 4*i + i;
std::string print;
print.assign(indent,' ');
for(auto btr:printContainer){
if (btr != nullptr) {
for(auto btv:btr->data){ //对于一个结点的所有data
print +=to_string(btv)+'|';
}
print.erase(print.end() - 1); // 去除掉最后一个'|'
print.append(5, ' ');
for(auto child:btr->children) {
storeContainer.push_back(child);
}
} else {
print += " ";
print.append(5,' ');
// FIXME: 这里需要修改
}
}
printContainer.erase(printContainer.begin(),printContainer.end());
printContainer.swap(storeContainer);
print.erase(print.end()-5);
print.append(indent,' ');
std::cout<<print<<std::endl;
}
}
int main(){
vector<int> sv = vector<int>{16,36,19,31,20,30,10,15,25,40,22,18,29,90,55,61};
cout<< "sv 元素尺寸数目:" << sv.size()<<endl;
lpBNode btree = builtBtree(sv);
remove(btree,40);
remove(btree,36);
remove(btree,15);
remove(btree,22);
remove(btree,25);
remove(btree,61);
show(btree);
return 0;
}
照例给出可视化网站观察效果:
https://www.cs.usfca.edu/~galles/visualization/BTree.html
来源:CSDN
作者:鼠二二
链接:https://blog.csdn.net/sinat_38230425/article/details/104344238