136 lines
3.3 KiB
C++
136 lines
3.3 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)
|
||
{
|
||
//1-删除叶子
|
||
}
|