379 lines
9.3 KiB
C++
379 lines
9.3 KiB
C++
#pragma once
|
||
#include "Node.h"
|
||
#include <iostream>
|
||
#include <algorithm>
|
||
|
||
template <class T>
|
||
class BPlusTree {
|
||
private:
|
||
Node<T>* root;
|
||
int M;
|
||
Node<T>* firstLeaf;//做链表范围查找
|
||
|
||
void splitChild(Node<T>* _node, int _i);
|
||
void insertNonFull(Node<T>* _node, const T& _key);
|
||
void erase(Node<T>* _node, const T& _key);
|
||
//void fixUnderflow(Node<T>* _node);没写 parent
|
||
T& getMin(Node<T>* _node);
|
||
|
||
void borrowLeft(Node<T>* _node, int _i);
|
||
void borrowRight(Node<T>* _node, int _i);
|
||
void mergeLeft(Node<T>* _node, int _i);
|
||
void mergeRight(Node<T>* _node, int _i);
|
||
|
||
public:
|
||
BPlusTree() = delete;
|
||
BPlusTree(int _M) : M(_M) {
|
||
root = new Node<T>(true);
|
||
firstLeaf = root;
|
||
}
|
||
|
||
void insert(const T& _key);
|
||
Node<T>* search(const T& _key);
|
||
void areaSearch(const T& _low, const T& _up);
|
||
void displayLeaf();
|
||
void erase(const T& _key);
|
||
|
||
};
|
||
|
||
template<class T>
|
||
inline void BPlusTree<T>::splitChild(Node<T>* _node, int _i)
|
||
{
|
||
Node<T>* child = _node->children[_i];
|
||
Node<T>* newc = new Node<T>(_node->children[_i]->isLeaf);
|
||
//中点,这个就是阶数的计算方式了,之前用t - 1 和 2t - 1
|
||
int mid = (M - 1) / 2;
|
||
//mid前面是左树,从mid开始往后是右树
|
||
//拷贝元素
|
||
newc->keys.assign(child->keys.begin() + mid, child->keys.end());
|
||
child->keys.resize(mid);
|
||
//拷贝孩子
|
||
if (!child->isLeaf) {
|
||
newc->children.assign(child->children.begin() + mid + 1, child->children.end());
|
||
child->children.resize(mid + 1);
|
||
}
|
||
//更新next节点
|
||
if (child->isLeaf) {
|
||
newc->next = child->next;
|
||
child->next = newc;
|
||
}
|
||
|
||
//比B树多的一步-更新_node,一般都是更新左(最小值)
|
||
//区别内部外部节点
|
||
if (!child->isLeaf)
|
||
//删掉newc第一个:例子-1,2,3,4,5,分为1,2;3,4,5,这时候推3,要把3从newc的第一个移除
|
||
newc->keys.erase(newc->keys.begin());
|
||
|
||
_node->keys.insert(_node->keys.begin() + _i, newc->keys.front());
|
||
_node->children.insert(_node->children.begin() + _i + 1, newc);
|
||
}
|
||
|
||
template<class T>
|
||
inline void BPlusTree<T>::insertNonFull(Node<T>* _node, const T& _key)
|
||
{
|
||
//是叶子节点直接插入
|
||
if (_node->isLeaf) {
|
||
auto it = std::lower_bound(_node->keys.begin(), _node->keys.end(), _key);
|
||
_node->keys.insert(it, _key); // 插入到第一个 >= _key 的位置
|
||
}
|
||
//不是叶子节点接着递归寻找
|
||
else {
|
||
auto it = std::lower_bound(_node->keys.begin(), _node->keys.end(), _key);
|
||
int idx = it - _node->keys.begin();
|
||
if (_node->children[idx]->keys.size() == M - 1) {
|
||
splitChild(_node, idx);
|
||
}
|
||
//插在哪边
|
||
if (_node->keys[idx] < _key) idx++;
|
||
insertNonFull(_node->children[idx], _key);
|
||
}
|
||
}
|
||
|
||
template<class T>
|
||
inline void BPlusTree<T>::erase(Node<T>* _node, const T& _key)
|
||
{
|
||
int mid = (M - 1) / 2;
|
||
if (!search(_key)) return;
|
||
if (!_node) return;
|
||
//前一半和search一样
|
||
//外部节点
|
||
if (_node->isLeaf) {
|
||
auto it = std::lower_bound(_node->keys.begin(), _node->keys.end(), _key);
|
||
int idx = it - _node->keys.begin();
|
||
if(it != _node->keys.end() && *it == _key)
|
||
_node->keys.erase(it);
|
||
//没事直接return
|
||
if (root == _node || _node->keys.size() >= mid){
|
||
return;
|
||
}
|
||
}
|
||
//内部节点
|
||
else {
|
||
auto it = std::lower_bound(_node->keys.begin(), _node->keys.end(), _key);
|
||
int idx = it - _node->keys.begin();
|
||
//若在索引,先替换本值为children[idx]的第二小值
|
||
if (it != _node->keys.end() && *it == _key) {
|
||
*it = getMin(_node->children[idx + 1]);
|
||
}
|
||
erase(_node->children[idx], _key);
|
||
|
||
//回溯的时候检查下溢,在这里检查(如果有parent节点就直接设置函数fixunderflow(_node)了)
|
||
Node<T>* child = _node->children[idx];
|
||
|
||
if (child->keys.size() < mid) {
|
||
//先借
|
||
// 借左兄弟
|
||
if (idx > 0 && _node->children[idx - 1]->keys.size() > mid) {
|
||
borrowLeft(_node, idx);
|
||
}
|
||
// 借右兄弟
|
||
else if (idx < _node->children.size() - 1 && _node->children[idx + 1]->keys.size() > mid) {
|
||
borrowRight(_node, idx);
|
||
}
|
||
// 借不到就合并
|
||
else if (idx > 0) {
|
||
mergeLeft(_node, idx); // 和左兄弟合并
|
||
}
|
||
else if (idx < _node->children.size() - 1) {
|
||
mergeRight(_node, idx); // 和右兄弟合并
|
||
}
|
||
}
|
||
}
|
||
}
|
||
/*
|
||
template<class T>
|
||
inline void BPlusTree<T>::fixUnderflow(Node<T>* _node)
|
||
{
|
||
//先找旁边借
|
||
|
||
//借不到就合并
|
||
}*/
|
||
|
||
template<class T>
|
||
inline T& BPlusTree<T>::getMin(Node<T>* _node)
|
||
{
|
||
if (!_node || _node->keys.empty()) {
|
||
throw std::runtime_error("Empty node has no minimum key");
|
||
}
|
||
|
||
Node<T>* cur = _node;
|
||
while (!cur->isLeaf) {
|
||
cur = cur->children[0]; // 重点:最小的,一直往左子树走
|
||
}
|
||
return cur->keys[0]; // 最左叶子的第一个 key
|
||
}
|
||
|
||
template<class T>
|
||
inline void BPlusTree<T>::borrowLeft(Node<T>* _node, int _i)
|
||
{
|
||
// 叶子节点
|
||
if (_node->isLeaf) {
|
||
T tmp = _node->children[_i - 1]->keys.back();
|
||
_node->children[_i - 1]->keys.pop_back();
|
||
_node->children[_i]->keys.insert(_node->children[_i]->keys.begin(), tmp);
|
||
_node->keys[_i - 1] = _node->children[_i]->keys[0]; // 更新父节点分界 key
|
||
}
|
||
// 内部节点
|
||
else {
|
||
Node<T>* left = _node->children[_i - 1];
|
||
Node<T>* cur = _node->children[_i];
|
||
|
||
// 父节点分界 key 移到当前节点首
|
||
cur->keys.insert(cur->keys.begin(), _node->keys[_i - 1]);
|
||
|
||
// 左兄弟最后 key 移到父节点
|
||
_node->keys[_i - 1] = left->keys.back();
|
||
left->keys.pop_back();
|
||
|
||
// 左兄弟最后子节点移到当前节点首(带着孩子一起动)
|
||
Node<T>* c = left->children.back();
|
||
left->children.pop_back();
|
||
cur->children.insert(cur->children.begin(), c);
|
||
}
|
||
}
|
||
|
||
template<class T>
|
||
inline void BPlusTree<T>::borrowRight(Node<T>* _node, int _i)
|
||
{
|
||
// 叶子节点
|
||
if (_node->isLeaf) {
|
||
Node<T>* cur = _node->children[_i];
|
||
Node<T>* right = _node->children[_i + 1];
|
||
|
||
// 右兄弟第一个 key 移到当前节点末尾
|
||
T tmp = right->keys.front();
|
||
right->keys.erase(right->keys.begin());
|
||
cur->keys.push_back(tmp);
|
||
|
||
// 更新父节点分界 key
|
||
_node->keys[_i] = right->keys.front();
|
||
}
|
||
// 内部节点
|
||
else {
|
||
Node<T>* cur = _node->children[_i];
|
||
Node<T>* right = _node->children[_i + 1];
|
||
|
||
// 父节点分界 key 移到当前节点末尾
|
||
cur->keys.push_back(_node->keys[_i]);
|
||
|
||
// 右兄弟第一个 key 移到父节点
|
||
_node->keys[_i] = right->keys.front();
|
||
right->keys.erase(right->keys.begin());
|
||
|
||
// 右兄弟第一个子节点移到当前节点末尾
|
||
Node<T>* c = right->children.front();
|
||
right->children.erase(right->children.begin());
|
||
cur->children.push_back(c);
|
||
}
|
||
}
|
||
|
||
/*
|
||
template<class T>
|
||
inline void BPlusTree<T>::mergeLeft(Node<T>* _node, int _i)
|
||
{
|
||
//处理父节点多出的东西
|
||
Node<T>* c = _node->children[_i];
|
||
_node->keys.erase(_node->keys.begin() + _i);
|
||
//合并
|
||
_node->children[_i - 1].insert(_node->children[_i - 1].end(), c.begin(), c.end());
|
||
_node->children[_i - 1]->children.insert(_node->children[_i - 1]->children.end(), c->children.begin(), c->children.end());
|
||
}
|
||
*/
|
||
|
||
template<class T>
|
||
inline void BPlusTree<T>::mergeLeft(Node<T>* _node, int _i)
|
||
{
|
||
Node<T>* left = _node->children[_i - 1];
|
||
Node<T>* cur = _node->children[_i];
|
||
|
||
// 1. 父节点 key 移动 / 删除
|
||
if (!left->isLeaf) {
|
||
// 内部节点:父节点分界 key 移到左兄弟末尾
|
||
left->keys.push_back(_node->keys[_i - 1]);
|
||
}
|
||
_node->keys.erase(_node->keys.begin() + (_i - 1));
|
||
|
||
// 2. 合并 keys
|
||
left->keys.insert(left->keys.end(), cur->keys.begin(), cur->keys.end());
|
||
|
||
// 3. 合并 children(仅内部节点)
|
||
if (!left->isLeaf) {
|
||
left->children.insert(left->children.end(), cur->children.begin(), cur->children.end());
|
||
}
|
||
|
||
// 4. 叶子节点更新 next 指针
|
||
if (left->isLeaf) {
|
||
left->next = cur->next;
|
||
}
|
||
|
||
// 5. 删除父节点 children 中的当前节点
|
||
_node->children.erase(_node->children.begin() + _i);
|
||
}
|
||
|
||
template<class T>
|
||
inline void BPlusTree<T>::mergeRight(Node<T>* _node, int _i)
|
||
{
|
||
Node<T>* cur = _node->children[_i];
|
||
Node<T>* right = _node->children[_i + 1];
|
||
|
||
// 1. 父节点 key
|
||
if (!cur->isLeaf) {
|
||
// 内部节点:父节点分界 key 移到当前节点末尾
|
||
cur->keys.push_back(_node->keys[_i]);
|
||
}
|
||
|
||
// 2. 合并 keys
|
||
cur->keys.insert(cur->keys.end(), right->keys.begin(), right->keys.end());
|
||
|
||
// 3. 合并 children(仅内部节点)
|
||
if (!cur->isLeaf) {
|
||
cur->children.insert(cur->children.end(), right->children.begin(), right->children.end());
|
||
}
|
||
|
||
// 4. 叶子节点更新 next 指针
|
||
if (cur->isLeaf) {
|
||
cur->next = right->next;
|
||
}
|
||
|
||
// 5. 删除父节点 key
|
||
_node->keys.erase(_node->keys.begin() + _i);
|
||
|
||
// 6. 删除父节点 children 中的右兄弟
|
||
_node->children.erase(_node->children.begin() + _i + 1);
|
||
}
|
||
|
||
template<class T>
|
||
inline void BPlusTree<T>::insert(const T& _key)
|
||
{
|
||
//分裂根
|
||
if (root->keys.size() == M - 1) {
|
||
Node<T>* newr = new Node<T>(false);
|
||
newr->children.push_back(root);
|
||
splitChild(newr, 0);
|
||
root = newr;
|
||
}
|
||
//插入
|
||
insertNonFull(root, _key);
|
||
}
|
||
|
||
template<class T>
|
||
inline Node<T>* BPlusTree<T>::search(const T& _key)
|
||
{
|
||
Node<T>* tmp = root;
|
||
while (tmp) {
|
||
auto it = std::lower_bound(tmp->keys.begin(), tmp->keys.end(), _key);
|
||
int idx = it - tmp->keys.begin();
|
||
|
||
if (tmp->isLeaf) {
|
||
//判断是否溢出,如果范围搜索需要用到,那么均需要返回tmp,单个索引的时候再根据返回的值搜索一遍即可
|
||
return tmp;
|
||
}
|
||
else {
|
||
tmp = tmp->children[idx];
|
||
}
|
||
}
|
||
return nullptr;
|
||
}
|
||
|
||
template<class T>
|
||
inline void BPlusTree<T>::areaSearch(const T& _low, const T& _up)
|
||
{
|
||
//最好不直接用search,返回的东西不一样
|
||
Node<T>* tmp = search(_low);
|
||
if (!tmp) return;
|
||
while (tmp) {
|
||
for (auto x : tmp->keys) {
|
||
if (x > _up) {
|
||
std::cout << std::endl;
|
||
return;
|
||
}
|
||
if (x >= _low) std::cout << x << " ";
|
||
}
|
||
tmp = tmp->next;
|
||
}
|
||
std::cout << std::endl;
|
||
}
|
||
|
||
template<class T>
|
||
inline void BPlusTree<T>::displayLeaf()
|
||
{
|
||
Node<T>* tmp = firstLeaf;
|
||
while (tmp) {
|
||
for (auto x : tmp->keys) std::cout << x << " ";
|
||
tmp = tmp->next;
|
||
std::cout << "C ";
|
||
}
|
||
std::cout << std::endl;
|
||
}
|
||
|
||
template<class T>
|
||
inline void BPlusTree<T>::erase(const T& _key)
|
||
{
|
||
if (!root) return;
|
||
//递归删除
|
||
erase(root, _key);
|
||
if (root->isLeaf && root->keys.size() == 0) {
|
||
root = root->children[0];
|
||
}
|
||
}
|