写了红黑树之后再写B树,可谓是轻松了不少,毕竟B树的分类情况比红黑树少太多。当然从效率上来说,我这个版本的B树实现效率肯定不是最高的,我是先删除移动后再做平衡,其实可以通过标识的方法在平衡里覆盖掉需要删除的点即可。当然写的时候为了思路更清晰,我并没有这样去写。还有个原因主要是,B树真的就是一种思路,就是通过这种结构算法,建立文件系统,从而大量数据搜索的时候,可以接近二分搜索的时间复杂度,优点就在于可以以片段的形式加载到内存,而且可以加载合适的大小,避免与硬盘多次IO交互的耗时。我写平衡的时候,还是尽量去避免了来回移动这个问题,不过还是有一定性能损失
#define M 4 //M个关键字 M+1阶 B树
#define DEF -2
#include<iostream>
#include<cstdlib>
#include <sstream>
#include<cmath>
using namespace std;
struct Node{
Node(){
_count=0;
for(int i=0;i<M+1;i++){
_data[i]=DEF;
_array[i]=0;
}
_array[M+1]=0;
}
int _count;
int _data[M+1];
Node* _array[M+2];
};
//用于叶节点中向后移动data
void move_back(int* start,int count,int step=1){
for(int i=count-1;i>=0;i--){
*(start+i+step)=*(start+i);
}
}
//用于分裂向上插入时父节点的向后移动
void move_back(Node* node,int start,int step=1){
node->_array[node->_count+step]=node->_array[node->_count];
for(int i=node->_count-1;i>=start;i--){
node->_data[i+step]=node->_data[i];
node->_array[i+step]=node->_array[i];
}
}
//用于叶节点中向前移动data
void move_front(int* start,int count,int step=1){
for(int i=0;i<count;i++){
*(start+i-step)=*(start+i);
}
}
//向前移动
void move_front(Node* node,int start,int step=1){
for(int i=start;i<node->_count;i++){
node->_data[i-step]=node->_data[i];
node->_array[i-step]=node->_array[i];
}
node->_array[node->_count-step]=node->_array[node->_count];
}
//二分定位
int getPos(Node* node,int data){
if(node==0){
return -1;
}
int min=0;
int max=node->_count-1;
if(max<0){
max=0;
}
while(min<max){
int mid=(min+max)/2;
if(data==node->_data[mid]){
return mid;
}
else if(data<node->_data[mid]){
max=mid-1;
}
else{
min=mid+1;
}
}
return min;
}
void print(Node* node){
if(node==0){
return;
}
cout<<"--------------------------"<<endl;
cout<<node<<':'<<node->_count<<endl;
for(int i=0;i<M+1;i++){
cout<<node->_data[i]<<' ';
}
cout<<endl;
for(int i=0;i<M+2;i++){
cout<<node->_array[i]<<' ';
}
cout<<endl;
cout<<"--------------------------"<<endl;
for(int i=0;i<M+2;i++){
print(node->_array[i]);
}
}
void insert(Node*& node,Node*& parent,int pindex,int data){
if(node->_count==0){
node->_data[0]=data;
node->_count++;
return;
}
int pos=getPos(node,data);
if(data<node->_data[pos]){
if(node->_array[0]==0){
if(pos<node->_count){
move_back(&node->_data[pos],node->_count-pos);
}
node->_data[pos]=data;
node->_count++;
}
else{
insert(node->_array[pos],node,pos,data);
}
}
else{
if(node->_array[0]==0){
if(pos+1<node->_count){
move_back(&node->_data[pos+1],node->_count-pos-1);
}
node->_data[pos+1]=data;
node->_count++;
}
else{
insert(node->_array[pos+1],node,pos+1,data);
}
}
//分裂检测
if(node->_count==M+1){
//构造兄弟节点
Node* sib=new Node;
int mid=M/2;
int start=0;
//将node右边 赋给sib
sib->_array[mid]=node->_array[M+1];
node->_array[M+1]=0;
for(int i=mid+1;i<M+1;i++,start++){
sib->_data[start]=node->_data[i];
sib->_array[start]=node->_array[i];
sib->_count++;
node->_data[i]=DEF;
node->_array[i]=0;
node->_count--;
}
int data=node->_data[mid];
node->_data[mid]=DEF;
node->_count--;
if(node==parent){
//构造父节点
Node* p=new Node;
p->_count=1;
p->_data[0]=data;
p->_array[0]=node;
p->_array[1]=sib;
parent=p;
}
else{
if(pindex<parent->_count){
move_back(parent,pindex);
//node 父节点从mid开始(包括node)整体被右移,但因为node右边已被移动到sib,所以必须在左边
parent->_array[pindex]=parent->_array[pindex+1];
}
parent->_data[pindex]=data;
parent->_array[pindex+1]=sib;
parent->_count++;
}
//print(parent);
}
}
void balance(Node* node,Node*& parent,int index){
if(node==parent){
if(parent->_count==0){
Node* temp=parent;
parent=parent->_array[0];
delete temp;
}
return;
}
//1.1 没有左兄弟 那么一定是最左边的节点,此时查找右兄 整个只判断一边就行了,两边太麻烦
if(index==0){
Node* rsib=parent->_array[index+1];
//1.1.1 兄弟有多于节点(节点数>=ceil(M+1)/2 -1 == 关键字数>=floor(M/2),现有关键字数应+1才能借位) 将父移动到删除节点,将兄弟上移动到父
if(rsib->_count-1>=floor(M/2.0)){
node->_data[node->_count]=parent->_data[index];
node->_count++;
node->_array[node->_count]=rsib->_array[0];
parent->_data[index]=rsib->_data[0];
move_front(rsib,1);
rsib->_array[parent->_array[index+1]->_count]=0;
rsib->_count--;
rsib->_data[parent->_array[index+1]->_count]=DEF;
}
//1.1.2 兄弟没有多余节点 父节点上对应元素移动到node,将兄弟移动到node,删除兄弟
else{
node->_data[node->_count]=parent->_data[index];
node->_count++;
int start=node->_count;
for(int i=0;i<rsib->_count;i++,start++){
node->_data[start]=rsib->_data[i];
node->_array[start]=rsib->_array[i];
node->_count++;
}
node->_array[node->_count]=rsib->_array[rsib->_count];
move_front(parent,1);
parent->_array[parent->_count]=0;
parent->_count--;
parent->_data[parent->_count]=DEF;
delete rsib;
parent->_array[0]=node;
}
}
else{
Node* lsib=parent->_array[index-1];
//1.2.1 兄弟有多于节点( 节点数>=ceil(M+1)/2 -1 == 关键字数>=floor(M/2),现有关键字数应+1才能借位) 将父移动到删除节点,将兄弟上移动到父
if(lsib->_count-1>=floor(M/2.0)){
move_back(node,0);
node->_data[0]=parent->_data[index-1];
node->_array[0]=lsib->_array[lsib->_count];
node->_count++;
parent->_data[index-1]=lsib->_data[lsib->_count-1];
lsib->_array[lsib->_count]=0;
lsib->_count--;
lsib->_data[lsib->_count]=DEF;
}
//1.2.2 兄弟没有多余节点 父节点上对应元素移动到兄弟,将node移动到兄弟,删除node
else{
lsib->_data[lsib->_count]=parent->_data[index-1];
lsib->_count++;
int start=lsib->_count;
for(int i=0;i<node->_count;i++,start++){
lsib->_data[start]=node->_data[i];
lsib->_array[start]=node->_array[i];
lsib->_count++;
}
lsib->_array[start]=node->_array[node->_count];
move_front(parent,index);
parent->_array[parent->_count]=0;
parent->_count--;
parent->_data[parent->_count]=DEF;
delete node;
parent->_array[index-1]=lsib;
}
}
}
void del_proc(Node* node,int pos){
if(pos+1<node->_count){
move_front(&node->_data[pos+1],node->_count-pos-1);
}
node->_count--;
node->_data[node->_count]=DEF;
}
void next(Node* node,Node*& parent,int& ori,int pos){
cout<<"next:"<<node<<endl;
if(node->_array[0]==0){
ori=node->_data[0];
del_proc(node,0);
}
else{
next(node->_array[0],node,ori,0);
}
//移动检测 根据B树定义,关键字数目应大于等于M/2
if(node->_count<ceil(M/2.0)){
balance(node,parent,pos);
}
}
void del(Node*& node,Node*& parent,int index,int data){
int pos=getPos(node,data);
if(pos==-1){
cout<<"i can't find it"<<endl;
return;
}
if(data==node->_data[pos]){
//处理节点
//1.如果是叶节点 直接把元素删除
if(node->_array[0]==0){
del_proc(node,pos);
}
//2.如果是内节点 需后继或前驱删除
else{
next(node->_array[pos+1],node,node->_data[pos],pos+1);
}
}
else if(data<node->_data[pos]){
del(node->_array[pos],node,pos,data);
}
else{
del(node->_array[pos+1],node,pos+1,data);
}
//移动检测 根据B树定义,关键字数目应大于等于M/2
if(node->_count<ceil(M/2.0)){
balance(node,parent,index);
}
}
void find(Node* node,int data,stringstream& ss){
int pos=getPos(node,data);
if(pos==-1){
cout<<"i can't find it"<<endl;
return;
}
if(data==node->_data[pos]){
ss<<pos;
cout<<"i find it:"<<data<<" by path:"<<ss.str()<<endl;
}
else if(data<node->_data[pos]){
ss<<pos<<',';
find(node->_array[pos],data,ss);
}
else{
ss<<pos+1<<',';
find(node->_array[pos+1],data,ss);
}
}
int main(){
srand(time(0));
Node* root=new Node;
for(int i=0;i<16;i++){
insert(root,root,0,rand()%32);
}
print(root);
int data;
while(true){
cout<<"cin the num you want to del:"<<endl;
cin>>data;
del(root,root,0,data);
print(root);
}
/*
int data;
stringstream ss;
while(true){
ss.str("");
cout<<"cin the num you want to find:"<<endl;
cin>>data;
find(root,data,ss);
}
*/
return 0;
}