B-树(最小I/O次数)

#include<iostream>
#include<cstdio>
#include<stdbool.h>
#include"bin_tree.h"                  // 用这里面的release
#include"vector.h"

using namespace std;
template<typename T> struct bt_node;
template<typename T> using bt_node_posi = bt_node<T>*;
template<typename T> struct bt_node
{
	bt_node_posi<T> parent;
	my_vector<T> key;
	my_vector<bt_node_posi<T>> child;
	// constructor
	bt_node()                                      // 0个关键码,1个孩子
	{
		child.insert(0, nullptr);
		parent = nullptr;
	}
	bt_node(T e,bt_node_posi<T> lc=nullptr,bt_node_posi<T> rc=nullptr)   // 1个关键码,2个孩子
	{
		parent = nullptr;
		key.insert(0, e);
		child.insert(0, lc);
		child.insert(1, rc);
		if (lc) lc->parent = this;      // 仍然是双向连接
		if (rc) rc->parent = this;
	}
};
/***************************************************B-tree******************************************************/
/***************************************************B-tree******************************************************/
template<typename T> class btree
{
protected:
	bt_node_posi<T> root, phit;
	int size;
	int order;                        // btree的阶数,constructor default:order==3
	void solve_overflow(bt_node_posi<T>);
	void solve_underflow(bt_node_posi<T>);
public:
	// constructor
	/*btree(int s=0, int Order=3,bt_node_posi<T> rot=nullptr, bt_node_posi<T> pht=nullptr):
		size(s),order(Order),root(rot),phit(pht){}*/
	btree(int Order = 3, int s = 0) :order(Order), size(s)
	{
		root = new bt_node<T>();
	}
	// destructor
	~btree()
	{
		//release(root);            // 搞不懂为何总是犯这种错
		if (root) release(root);
	}
	int get_size() { return size; }
	int get_order() { return order; }
	bt_node_posi<T> get_root() { return root; }
	bool empty() { if (size == 0) return true; else return false; }
	
	void inorder_tra(bt_node_posi<T> ptr);
	// 以下为 B树 的3个操作接口:查找,插入,删除
	bt_node_posi<T> search(const T& e);
	bool insert(const T& e);
	bool remove(const T& e);
};
int cnt = 0;
template<typename T> void btree<T>::inorder_tra(bt_node_posi<T> ptr)
{
	if (ptr == nullptr) return;
	//if (ptr->child[0] == nullptr) {             // 节点为叶子节点
	//	ptr->key.show();
	//}
	for (int i = 0; i < ptr->child.Size(); ++i) {
		// cout << "key.size=" << ptr->key.Size() << endl;
		bt_node_posi<T> temp = ptr->child[i];
		inorder_tra(temp);
		if (i < ptr->key.Size()) {
			cout << ptr->key[i] << " ";
			/*if (++cnt % 20 == 0) cout << endl;*/
		}
	}
}
template<typename T> bt_node_posi<T> btree<T>::search(const T& e)
{
	bt_node_posi<T> ptr = root;
	phit = nullptr;                                      // 同BST中的phit,search最后访问的非空节点的位置
	while (ptr)
	{
		Rank r = ptr->key.search(e);
		if (r>=0 && ptr->key[r] == e) return ptr;         // 找到了的话,返回其所属的bt_node的位置
		else
		{
			phit = ptr;
			ptr = ptr->child[r + 1];
		}
	}
	//return phit;    // 这里与bst不同,search 里面有phit为之后的insert做准备,失败直接返回null即可
	return nullptr;
}
template<typename T> bool btree<T>::insert(const T& e)
{
	if (search(e)) return false;                  // 树中有相同的就不用再插了
	//Rank r = phit->key->search(e);              // 这里的key是向量呀,怎么还用->呢
	Rank r = phit->key.search(e);
	phit->key.insert(r + 1, e);                   // r是不大于e的最大rank
	phit->child.insert(r + 2, nullptr);           // 插入都是在叶节点插的
	++size;
	solve_overflow(phit);
	return true;
}
template<typename T> bool btree<T>::remove(const T& e)
{
	bt_node_posi<T> ptr = search(e);
	if (!ptr) return false;
	Rank r = ptr->key.search(e);
	if (ptr->child[0] != nullptr)                              // 此时为非叶节点,需要转换成叶节点
	{
		bt_node_posi<T> suc = ptr->child[r + 1];
		while (suc->child[0] != nullptr) suc = suc->child[0];
		ptr->key[r] = suc->key[0];
		/*suc->key.remove(0);
		suc->child.remove(0);
		solve_underflow(suc);*/                              // 你不觉得这样写重复了吗
		ptr = suc;
		r = 0;
	}
	ptr->key.remove(r);
	ptr->child.remove(r + 1);
	--size;
	solve_underflow(ptr);                        // search 说明找到了要删除节点的那个对应的超级节点(必不为null)
	return true;
}
template<typename T> void btree<T>::solve_overflow(bt_node_posi<T> ptr)
{
	do{
		if (ptr->key.Size() < order) break;                     // order==m,如果key==m,则溢出
		int mid = order / 2;
		// 拷贝右半边向量
		bt_node_posi<T> rchild = new bt_node<T>();
		/*for (int i = mid+1;i<ptr->key.Size();++i)
		{
			rchild->key.insert(rchild->key.Size(), ptr->key.remove(i)); 
			rchild->child.insert()                           // 这样一个一个插入删除,效率太低
		}*/
		my_vector<T> k(ptr->key, mid + 1, ptr->key.Size());
		rchild->key = k;                                      // 区间拷贝
		ptr->key.remove(mid + 1, ptr->key.Size());             // 区间删除
		my_vector<bt_node_posi<T>> ch(ptr->child, mid + 1, ptr->child.Size());
		rchild->child = ch;                                    // 拷贝构造函数+赋值重载函数
		ptr->child.remove(mid + 1, ptr->child.Size());
		if (rchild->child[0])
			for (int i = 0; i < rchild->child.Size() ; ++i)   // 指针回连,这里刚开始写错了!!!
				rchild->child[i]->parent = rchild;
		// 将节点插入到父节点中去
		bt_node_posi<T> parent = ptr->parent;
		if (parent != nullptr) {
			Rank r = parent->key.search(ptr->key[mid]);          // 在父节点找出恰当的位置
			parent->key.insert(r + 1, ptr->key.remove(mid));   // 父节点中插入相应的节点,子节点删除对应元素
			parent->child.insert(r + 2, rchild);
			rchild->parent = parent;               
		}
		else
		{
			parent = root = new bt_node<T>();
			parent->key.insert(0, ptr->key.remove(mid));
			parent->child[0] = ptr;                            // bt_node 构造函数就已经有一个空指针了
			parent->child.insert(1, rchild);
			ptr->parent = parent;
			rchild->parent = parent;
		}
		ptr = ptr->parent;
	}while (ptr != nullptr);
}
template<typename T> void btree<T>::solve_underflow(bt_node_posi<T> ptr)
{
	bt_node_posi<T> parent;
	do {
		if (ptr->key.Size() >= order / 2)
			return;
		parent = ptr->parent;
		Rank r = 0;
		if (parent)                                // ptr不是根节点
		{
			// 一个一个遍历
			for (int i = 0; i < parent->child.Size(); ++i)       // 等价 while(parent->child[r]!=ptr) r++;
				if (parent->child[i] == ptr) 
					r = i;
			// 左边够借
			if (r >= 1 && parent->child[r - 1]->key.Size() > order / 2)
			{
				bt_node_posi<T> left = parent->child[r - 1];
				ptr->key.insert(0, parent->key[r - 1]);                              // parent-->ptr
				ptr->child.insert(0, left->child.remove(left->child.Size()-1));
				//left->child[left->child.Size() - 1]->parent = ptr;           // 别这么复杂
				if(ptr->child[0]) ptr->child[0]->parent = ptr;                 // 每次都是这个if出错了!!!
				parent->key[r - 1] = left->key.remove(left->key.Size() - 1);       // left-->parent
			}// 右边够借
			else if (r<parent->child.Size() - 1 && parent->child[r + 1]->key.Size()>order / 2)	    										// 如果右边够借
			{
				bt_node_posi<T> right = parent->child[r + 1];
				ptr->key.insert(ptr->key.Size(), parent->key[r]);
				ptr->child.insert(ptr->child.Size(), right->child.remove(0));
				//if(right->child[0]) right->child[0]->parent = ptr;         // 这里弄错了(你认为是等价的其实不等价)!
				if (ptr->child[ptr->child.Size() - 1]) ptr->child[ptr->child.Size() - 1]->parent = ptr;
				parent->key[r] = right->key.remove(0);
			}
			else    // 都不够借                                                                
			{
				if (r == parent->key.Size() )                                  // 只能往左边合并
				{
					bt_node_posi<T> left = parent->child[r - 1];
					left->key.insert(left->key.Size(), parent->key.remove(r - 1));
					parent->child.remove(r);
					for (int i = 0; i < ptr->key.Size(); ++i)                    // 合并
					{
						left->key.insert(left->key.Size(), ptr->key.remove(0));
						//if(ptr->child[0]) ptr->child[0]->parent = left;         //同203
						left->child.insert(left->child.Size(), ptr->child.remove(0));
						if (left->child[left->child.Size() - 1])  left->child[left->child.Size() - 1]->parent = left;
					}
					// 222 223 这两行顺序别搞错了
					left->child.insert(left->child.Size(), ptr->child.remove(0));
					if (left->child[left->child.Size() - 1]) left->child[left->child.Size() - 1]->parent = left;
					release(ptr);
				}
				else                                                              // 一般情况,往右边合并
				{
					bt_node_posi<T> right = parent->child[r + 1];
					ptr->key.insert(ptr->key.Size(), parent->key.remove(r));
					parent->child.remove(r + 1);
					for (int i = 0; i < right->key.Size(); ++i)
					{
						ptr->key.insert(ptr->key.Size(), right->key.remove(0));
						// if (right->child[0]) right->child[0]->parent = ptr;   // 同203
						ptr->child.insert(ptr->child.Size(), right->child.remove(0));
						if (ptr->child[ptr->child.Size() - 1]) ptr->child[ptr->child.Size() - 1]->parent = ptr;
					}
					/*if(right->child[0]) right->child[0]->parent = ptr;*/
					ptr->child.insert(ptr->child.Size(), right->child.remove(0));
					if (ptr->child[ptr->child.Size() - 1]) ptr->child[ptr->child.Size() - 1]->parent = ptr;
					release(right);
				}
			}
		}
		else                                                    // ptr 是根节点(分裂至根节点或者仅有根节点)
		{
			if (ptr->key.Size() == 0 && ptr->child[0] != nullptr)
			{
				root = ptr->child[0];
				root->parent = nullptr;
				ptr->child[0] = nullptr;
				release(ptr);
			}
		}
		//ptr = ptr->parent;                   // 进入else 分支,这么写的话就得凉凉
		ptr = parent;
	} while (parent != nullptr);
}
int main()
{
	btree<int> Btree(3);
	for (int i = 1; i < 100; ++i)
	{
		Btree.insert(i);
	}
	Btree.inorder_tra(Btree.get_root());
	cout << endl;
	cout << "size=" << Btree.get_size() << endl;
	for (int i = 0; i < 30; ++i)
	{
		int del_num = 61 + i;
		cout << endl << del_num << endl;
		Btree.remove(del_num);
		cout << endl;
		Btree.inorder_tra(Btree.get_root());
		cout << endl;
		/*Btree.remove(rand() % 100 + 1);
		cout << endl;
		Btree.inorder_tra(Btree.get_root());
		cout << endl;*/
	}
}

/*番外篇:总结*/
/*
*01.关于向量的有序查找:实践表明:在B树的每一个大节点一般与内存块中的一页大小相对应(几个kb)
*也就是说每一个大节点下的向量个数也就几百个,在这种规模下,直接用顺序查找即可,不必再使用二分查找
* 我在重新写顺序查找的时候,追求代码的简洁,导致如此基本的操作频频出错(eg.忽略了最基本的语义,以及初始条件的判定)
* 没那个水平就认认真真去写,结果正确才是王道
* 
* 02.回顾solve_overflow
* 有几个点是值得注意的: 关于向量的区间拷贝,本来是打算使用赋值重载函数的,但是无奈用不了
* my_vector& operator=(my_vector<T> const& v,int low,int high); 在调用的时候v1=(v2,low,high)变成了
* 逗号表达式,与最初的语义相违背,所以只能采用 部分拷贝构造+赋值重载了
* 
* 03.回顾solve_underflow
* 每次连接其parent域时,一定要先判断其是否为null
* btree内部的指针与其外部指针是两类不同类型的,指针连接是内部指针的连接,eg:203行
* search接口语义这次我写的不完整,导致后面debug异常艰辛,纠正的代价是极其大的,每一个细节都得仔细考虑
*/

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值