/**
* @author 李亚杰
* @date 2024/7/4 上午8:53
* @description BTree
*/
public class BTree {
final int MIN_KEY_NUMBER; //最小key数目
final int MAX_KEY_NUMBER; //最大key数目
Node root;
int t;//最小度数
public BTree(int t) {
this.t = t;
root = new Node(t);
MAX_KEY_NUMBER = 2 * t - 1;
MIN_KEY_NUMBER = t - 1;
}
public BTree() {
this(2);
}
private static boolean found(Node node, int key, int i) {
return i < node.keyNumber && node.keys[i] == key;
}
//1.是否存在
public boolean contains(int key) {
return root.get(key) != null;
}
//2.新增
public void put(int key) {
doPut(root, key, null, 0);
}
private void doPut(Node node, int key, Node parent, int index) {
int i = 0;
while (i < node.keyNumber) {
if (node.keys[i] == key) {
//更新
return;
}
if (node.keys[i] > key) {
break; //找到插入位置 i
}
i++;
}
if (node.leaf) {
node.insertKey(key, i);
} else {
doPut(node.children[i], key, node, i);
}
if (node.keyNumber == MAX_KEY_NUMBER) {
split(node, parent, index);
}
}
void split(Node left, Node parent, int index) {
//根节点
if (parent == null) {
Node newRoot = new Node(t);
newRoot.leaf = false;
newRoot.insertChild(left, 0);
this.root = newRoot;
parent = newRoot;
}
//创建right节点,把t以后的key和Child都拷贝过去
Node right = new Node(t);
right.leaf = left.leaf;
//拷贝元素
System.arraycopy(left.keys, t, right.keys, 0, t - 1);
if (!left.leaf) {
System.arraycopy(left.children, t, right.children, 0, t);
}
right.keyNumber = t - 1;
left.keyNumber = t - 1;
//中间的key(t-1处)插入到父节点
int mid = left.keys[t - 1];
parent.insertKey(mid, index);
//right节点作为父节点的孩子
parent.insertChild(right, index + 1);
}
/**
* 打印树中的所有节点元素
*/
void print() {
Node p = root;
int size = 1;
LinkedList<Node> queue = new LinkedList<>();
queue.addLast(p);
while (!queue.isEmpty()) {
for (int i = 0; i < size; i++) {
Node node1 = queue.removeFirst();
System.out.print(node1);
if (!node1.leaf) {
for (int j = 0; j <= node1.keyNumber; j++) {
queue.addLast(node1.children[j]);
}
}
}
size = queue.size();
System.out.println();
}
}
public void remove(int key) {
doRemove(root, key,null,0);
}
/**
*
* @param node 被查找的节点
* @param key 被查找key
* @param parent 被查找节点的父亲
* @param index 被查找节点在父亲的索引
*/
private void doRemove(Node node, int key,Node parent,int index) {
int i = 0;
while (i < node.keyNumber) {
if (node.keys[i] >= key) {
break;
}
i++;
}
//找到 i == 待删除的索引
//未找到 到第i个孩子继续查找
if (node.leaf) { //叶子节点
if (!found(node, key, i)) {//case1 叶子节点,没找到
return;
} else {//case2 叶子节点,找到了
node.removeKey(i);
}
} else {//不是叶子节点
if (!found(node, key, i)) {//case3 非叶子节点,没找到
doRemove(node.children[i], key,node,i);
} else {//case4 非叶子节点,找到了
Node s = node.children[i+1];
while (!s.leaf){
s = s.children[0];
}
int skey = s.keys[0];//后继key
//替换待删除key
node.keys[i] = skey;
//删除被替换的key
doRemove(node.children[i+1],skey,node,i+1);
}
}
if (node.keyNumber < MIN_KEY_NUMBER) {//case5,6 删除后的节点数目 < 下限,调整节点。
balance(parent,node,index);
}
}
/**
* 调整树节点
* @param parent 被调整节点父节点
* @param x 被调整节点
* @param i 被调整节点的索引
*/
private void balance(Node parent,Node x,int i){
//case6 根节点
if(x==root){
if (root.keyNumber==0 && root.children[0]!=null){
root = root.children[0];
}
return;
}
Node left = parent.childLeftSibling(i);
Node right = parent.childRightSibling(i);
//case 5-1 左边富裕,右旋
if (left!=null&&left.keyNumber>MIN_KEY_NUMBER){
// 父节点中后继key旋转下来
x.insertKey(parent.keys[i - 1],0);
//left最大孩子换爹
if (!left.leaf){
x.insertChild(left.removeRightmostChild(),0);
}
//left最大key换爹
parent.keys[i-1] = left.removeRightmostKey();
return;
}
//case 5-2 右边富裕,左旋
if (right!=null&&right.keyNumber>MIN_KEY_NUMBER){
x.insertKey(parent.keys[i],x.keyNumber);
if (!right.leaf){
x.insertChild(right.removeLeftmostChild(), x.keyNumber);
}
parent.keys[i] = right.removeLeftmostKey();
return;
}
//case 5-3 两边都不够,向左合并
if (left!=null){
//向左兄弟合并
//删除自己
parent.removeChild(i);
// 将父节点key添加的兄弟
left.insertKey(parent.removeKey(i-1), left.keyNumber);
//将自己所有元素添加到左兄弟
x.moveToTarget(left);
}else {
//向自己合并
parent.removeChild(i+1);
x.insertKey(parent.removeKey(i),x.keyNumber);
right.moveToTarget(x);
}
}
static class Node {
int[] keys;//关键字
Node[] children;//孩子
int keyNumber;//有效关键字的个数
boolean leaf = true;//是否是叶子节点
int t;// 最小度数
public Node(int t) {
this.t = t;
this.children = new Node[2 * t];
this.keys = new int[2 * t - 1];
}
public Node(int[] keys) {
this.keys = keys;
this.keyNumber = keys.length;
}
@Override
public String toString() {
return Arrays.toString(Arrays.copyOfRange(keys, 0, keyNumber));
}
//多路查找
Node get(int key) {
int i = 0;
while (i < keyNumber) {
if (keys[i] == key) {
return this;
}
if (keys[i] > key) {
break;
}
i++;
}
//如果是叶子节点,说明没有此元素
if (leaf) {
return null;
}
return children[i].get(key);
}
//向keys指定索引处插入key
void insertKey(int key, int index) {
System.arraycopy(keys, index, keys, index + 1, keyNumber - index);
keys[index] = key;
keyNumber++;
}
//向children 指定索引插入child
void insertChild(Node child, int index) {
System.arraycopy(children, index, children, index + 1, keyNumber - index);
children[index] = child;
}
//移除指定index处的key
int removeKey(int index) {
int t = keys[index];
System.arraycopy(keys, index + 1, keys, index, --keyNumber - index);
return t;
}
//移除最左边的key
int removeLeftmostKey() {
return removeKey(0);
}
//移除最右边的key
int removeRightmostKey() {
return removeKey(keyNumber - 1);
}
// 移除指定index处的Child
Node removeChild(int index) {
Node removed = children[index];
System.arraycopy(children, index + 1, children, index, keyNumber - index);
return removed;
}
Node removeLeftmostChild() {
return removeChild(0);
}
Node removeRightmostChild() {
return removeChild(keyNumber - 1);
}
//index左边的Child
Node childLeftSibling(int index) {
return index > 0 ? children[index - 1] : null;
}
Node childRightSibling(int index) {
return index != keyNumber ? children[index + 1] : null;
}
void moveToTarget(Node target) {
int start = target.keyNumber;
if (!leaf) {
System.arraycopy(children, 0, target.children, start, keyNumber + 1);
}
System.arraycopy(keys, 0, target.keys, start, keyNumber);
target.keyNumber += keyNumber;
}
}
}
测试用例
@Test
void remove(){
BTree tree = new BTree(2);
for (int i = 1; i < 7; i++) {
tree.put(i);
}
tree.print();
tree.remove(2);
tree.print();
tree.remove(6);
tree.print();
tree.remove(1);
tree.print();
tree.remove(3);
tree.print();
tree.remove(4);
tree.remove(5);
tree.print();
}