#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异常艰辛,纠正的代价是极其大的,每一个细节都得仔细考虑
*/
“相关推荐”对你有帮助么?
-
非常没帮助
-
没帮助
-
一般
-
有帮助
-
非常有帮助
提交