C++ B树

写了红黑树之后再写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;
}

    原文作者:B树
    原文地址: https://blog.csdn.net/nightwizard2030/article/details/77451712
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注