Files
Data-Structure/BinaryTree/BalanceTree/B-Tree/BTree.h
2025-08-26 19:21:23 +08:00

300 lines
11 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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 = 2root 目前有 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++;
// ==============================================================
// 情况 1key 存在于当前节点 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);
}
}
}
// ==============================================================
// 情况 2key 不在当前节点 → 去子树里找
// ==============================================================
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;
}
}