300 lines
11 KiB
C++
300 lines
11 KiB
C++
#pragma once
|
||
#include <iostream>
|
||
#include "Node.h"
|
||
#include <algorithm>
|
||
template <class T>
|
||
class BTree {
|
||
private:
|
||
Node<T>* root;
|
||
int t; //*B-树的阶数(节点最大元素数的一半)
|
||
//这里的t最后体现在每个节点上都是t - 1 到 2t - 1
|
||
|
||
void splitChild(Node<T>* _node, int _n);
|
||
void insertNonFull(Node<T>* _node, const T& _key);
|
||
void inorder(Node<T>* _n) {
|
||
if (!_n) return;
|
||
|
||
int i = 0;
|
||
//关注中序的时候:inorder(children[0]) -> inorder(key[0]) -> inorder(children[1])... 这样保证升序
|
||
for (i = 0; i < _n->keys.size(); i++) {
|
||
if (i < _n->children.size()) inorder(_n->children[i]);
|
||
std::cout << _n->keys[i] << " ";
|
||
}
|
||
//已经i++
|
||
if (i < _n->children.size()) inorder(_n->children[i]);
|
||
std::cout << std::endl;
|
||
}
|
||
public:
|
||
BTree() = delete;
|
||
BTree(int _t) : t(_t), root(nullptr) {}
|
||
|
||
void display() { inorder(root); }
|
||
void insert(const T& _key);
|
||
bool search(Node<T>* _node, const T& _key);
|
||
void erase(const T& _key);
|
||
};
|
||
|
||
template<class T>
|
||
inline void BTree<T>::splitChild(Node<T>* _pt, int _i)
|
||
{
|
||
//_pt-父节点//_i-第几个子节点//t-中间元素
|
||
Node<T>* p = _pt->children[_i];
|
||
Node<T>* tmp = new Node<T>(p->isLeaf);
|
||
int mid = t - 1;
|
||
//复制mid右侧
|
||
for (int i = 0; i < mid; i++) {
|
||
tmp->keys.push_back(p->keys[t + i]);
|
||
}
|
||
//不是叶子节点-复制孩子
|
||
if (!p->isLeaf) {
|
||
for (int i = 0; i < t; i++) {
|
||
tmp->children.push_back(p->children[i + t]);
|
||
}
|
||
}
|
||
T middlekey = p->keys[mid];
|
||
//保留左侧
|
||
p->keys.resize(mid);
|
||
if (!p->isLeaf) p->children.resize(t);//t = mid + 1
|
||
|
||
//增加孩子
|
||
_pt->children.insert(_pt->children.begin() + _i + 1, tmp);
|
||
//提取中间元素插入上面
|
||
_pt->keys.insert(_pt->keys.begin() + _i, middlekey);
|
||
}
|
||
|
||
template<class T>
|
||
inline void BTree<T>::insertNonFull(Node<T>* _node, const T& _key)
|
||
{
|
||
int i = _node->keys.size() - 1;
|
||
//是叶子节点-先插入后排序O(n)
|
||
if (_node->isLeaf) {
|
||
_node->keys.push_back(_key);
|
||
//向后移位
|
||
while (i >= 0 && _node->keys[i] > _key) {
|
||
_node->keys[i + 1] = _node->keys[i];
|
||
i--;
|
||
}
|
||
_node->keys[i + 1] = _key;
|
||
}
|
||
//不是叶子节点,递归向下寻找叶子节点
|
||
else {
|
||
while (i >= 0 && _node->keys[i] > _key) {
|
||
i--;
|
||
}
|
||
i++;//找到children位置
|
||
//节点满了先分裂
|
||
if (_node->children[i]->keys.size() == 2 * t - 1) {
|
||
splitChild(_node, i);
|
||
//分裂提升后可能改变关键字位置
|
||
if (_key > _node->keys[i]) i++;
|
||
}
|
||
insertNonFull(_node->children[i], _key);
|
||
}
|
||
}
|
||
|
||
template<class T>
|
||
inline void BTree<T>::insert(const T& _key)
|
||
{
|
||
//1-树空无root
|
||
if (!root) {
|
||
root = new Node<T>(true);
|
||
root->keys.push_back(_key);
|
||
}
|
||
//2-有root正常插入
|
||
else {
|
||
//root满,分裂根节点
|
||
//重点关注:插入的时候,不管关键字是否上升到根节点,如果根节点满了,都要分裂以防万一
|
||
if (root->keys.size() == 2*t - 1) {
|
||
Node<T>* _root = new Node<T>(false);
|
||
_root->children.push_back(root);
|
||
splitChild(_root, 0);
|
||
root = _root;
|
||
}
|
||
//继续执行
|
||
insertNonFull(root, _key);
|
||
}
|
||
//当 t = 2,root 目前有 2 个元素,插入一个新元素后,root 会变成 3 个元素(满节点)。此时还不会分裂,分裂逻辑只在下一次插入超过 3 个元素时触发。
|
||
}
|
||
|
||
template<class T>
|
||
inline bool BTree<T>::search(Node<T>* _node, const T& _key)
|
||
{
|
||
//可以递归做
|
||
Node<T>* tmp = _node;
|
||
int i = 0;
|
||
while (tmp->keys[i] < _key && i < tmp->keys.size()) i++;
|
||
if (tmp->keys[i] == _key) return true;
|
||
if(tmp->isLeaf) return false;
|
||
return search(_node->children[i], _key);
|
||
}
|
||
|
||
template<class T>
|
||
inline void BTree<T>::erase(const T& _key)
|
||
{
|
||
if (!root) return; // 空树,直接返回
|
||
|
||
// 用一个递归 lambda 来实现删除逻辑
|
||
auto remove = [&](auto&& self, Node<T>* node, const T& key) -> void {
|
||
int idx = 0;
|
||
// 找到第一个 >= key 的位置
|
||
while (idx < node->keys.size() && node->keys[idx] < key) idx++;
|
||
|
||
// ==============================================================
|
||
// 情况 1:key 存在于当前节点 node->keys[idx]
|
||
// ==============================================================
|
||
if (idx < node->keys.size() && node->keys[idx] == key) {
|
||
// ---- 1.1 如果当前节点是叶子:直接删除即可
|
||
if (node->isLeaf) {
|
||
node->keys.erase(node->keys.begin() + idx);
|
||
}
|
||
// ---- 1.2 如果是内部节点(非叶子)
|
||
else {
|
||
Node<T>* leftChild = node->children[idx];
|
||
Node<T>* rightChild = node->children[idx + 1];
|
||
|
||
// ① 如果左子树至少有 t 个关键字(够借用)
|
||
if (leftChild->keys.size() >= t) {
|
||
// 找到前驱(左子树最右边元素)
|
||
Node<T>* cur = leftChild;
|
||
while (!cur->isLeaf) cur = cur->children.back();
|
||
T pred = cur->keys.back();
|
||
// 用前驱替换当前 key
|
||
node->keys[idx] = pred;
|
||
// 递归去左子树删掉前驱
|
||
self(self, leftChild, pred);
|
||
}
|
||
// ② 如果右子树至少有 t 个关键字(够借用)
|
||
else if (rightChild->keys.size() >= t) {
|
||
// 找到后继(右子树最左边元素)
|
||
Node<T>* cur = rightChild;
|
||
while (!cur->isLeaf) cur = cur->children.front();
|
||
T succ = cur->keys.front();
|
||
// 用后继替换当前 key
|
||
node->keys[idx] = succ;
|
||
// 递归去右子树删掉后继
|
||
self(self, rightChild, succ);
|
||
}
|
||
// ③ 左右子树都只有 t-1 个关键字,无法借 → 合并
|
||
else {
|
||
// 把 key 下沉到左子树
|
||
leftChild->keys.push_back(node->keys[idx]);
|
||
// 把右子树的 keys 全部并入左子树
|
||
leftChild->keys.insert(leftChild->keys.end(),
|
||
rightChild->keys.begin(), rightChild->keys.end());
|
||
// 如果不是叶子,还要把右子树的 children 并过去
|
||
if (!leftChild->isLeaf) {
|
||
leftChild->children.insert(leftChild->children.end(),
|
||
rightChild->children.begin(), rightChild->children.end());
|
||
}
|
||
// 删除父节点中的 key 和右子树指针
|
||
node->keys.erase(node->keys.begin() + idx);
|
||
node->children.erase(node->children.begin() + idx + 1);
|
||
delete rightChild;
|
||
// 继续在合并后的左子树中删除 key
|
||
self(self, leftChild, key);
|
||
}
|
||
}
|
||
}
|
||
// ==============================================================
|
||
// 情况 2:key 不在当前节点 → 去子树里找
|
||
// ==============================================================
|
||
else {
|
||
if (node->isLeaf) return; // 到叶子还没找到,说明树里没有,直接返回
|
||
|
||
bool flag = (idx == node->keys.size()); // key 比所有节点都大 → 在最后一个孩子
|
||
Node<T>* child = node->children[idx];
|
||
|
||
// ----------------------------------------------------------
|
||
// 删除之前要保证 child 至少有 t 个关键字(否则可能删完不合法)
|
||
// ----------------------------------------------------------
|
||
if (child->keys.size() < t) {
|
||
Node<T>* leftSibling = (idx > 0 ? node->children[idx - 1] : nullptr);
|
||
Node<T>* rightSibling = (idx < node->keys.size() ? node->children[idx + 1] : nullptr);
|
||
|
||
// ① 如果左兄弟存在且有 >= t 个关键字 → 向左兄弟借
|
||
if (leftSibling && leftSibling->keys.size() >= t) {
|
||
// 父节点下移一个 key 给 child
|
||
child->keys.insert(child->keys.begin(), node->keys[idx - 1]);
|
||
// 左兄弟最后一个 key 上移到父节点
|
||
node->keys[idx - 1] = leftSibling->keys.back();
|
||
leftSibling->keys.pop_back();
|
||
// 如果有子节点,别忘了调整 children
|
||
if (!leftSibling->isLeaf) {
|
||
child->children.insert(child->children.begin(), leftSibling->children.back());
|
||
leftSibling->children.pop_back();
|
||
}
|
||
}
|
||
// ② 如果右兄弟存在且有 >= t 个关键字 → 向右兄弟借
|
||
else if (rightSibling && rightSibling->keys.size() >= t) {
|
||
// 父节点下移一个 key 给 child
|
||
child->keys.push_back(node->keys[idx]);
|
||
// 右兄弟第一个 key 上移到父节点
|
||
node->keys[idx] = rightSibling->keys.front();
|
||
rightSibling->keys.erase(rightSibling->keys.begin());
|
||
// 如果有子节点,别忘了调整 children
|
||
if (!rightSibling->isLeaf) {
|
||
child->children.push_back(rightSibling->children.front());
|
||
rightSibling->children.erase(rightSibling->children.begin());
|
||
}
|
||
}
|
||
// ③ 左右兄弟都不足 t → 合并
|
||
else {
|
||
if (leftSibling) {
|
||
// 把父节点一个 key 下移到左兄弟
|
||
leftSibling->keys.push_back(node->keys[idx - 1]);
|
||
// 把 child 的 keys 全部并到左兄弟
|
||
leftSibling->keys.insert(leftSibling->keys.end(),
|
||
child->keys.begin(), child->keys.end());
|
||
if (!child->isLeaf) {
|
||
leftSibling->children.insert(leftSibling->children.end(),
|
||
child->children.begin(), child->children.end());
|
||
}
|
||
// 父节点删除 key 和 child 指针
|
||
node->keys.erase(node->keys.begin() + idx - 1);
|
||
node->children.erase(node->children.begin() + idx);
|
||
delete child;
|
||
child = leftSibling;
|
||
}
|
||
else if (rightSibling) {
|
||
// 把父节点一个 key 下移到 child
|
||
child->keys.push_back(node->keys[idx]);
|
||
// 把右兄弟的 keys 全部并入 child
|
||
child->keys.insert(child->keys.end(),
|
||
rightSibling->keys.begin(), rightSibling->keys.end());
|
||
if (!rightSibling->isLeaf) {
|
||
child->children.insert(child->children.end(),
|
||
rightSibling->children.begin(), rightSibling->children.end());
|
||
}
|
||
node->keys.erase(node->keys.begin() + idx);
|
||
node->children.erase(node->children.begin() + idx + 1);
|
||
delete rightSibling;
|
||
}
|
||
}
|
||
}
|
||
// ----------------------------------------------------------
|
||
// 保证 child 至少有 t 个 key 后,递归下去
|
||
// ----------------------------------------------------------
|
||
if (flag && idx > node->keys.size())
|
||
self(self, node->children[idx - 1], key);
|
||
else
|
||
self(self, node->children[idx], key);
|
||
}
|
||
};
|
||
|
||
// 调用删除逻辑
|
||
remove(remove, root, _key);
|
||
|
||
// ==============================================================
|
||
// 删除后可能导致根节点为空
|
||
// 需要调整:如果 root 没有 key 了
|
||
// ==============================================================
|
||
if (root->keys.empty()) {
|
||
Node<T>* oldRoot = root;
|
||
// 如果 root 是叶子 → 整棵树为空
|
||
if (root->isLeaf) root = nullptr;
|
||
else root = root->children[0]; // root 的唯一孩子成为新的根
|
||
delete oldRoot;
|
||
}
|
||
}
|
||
|