package com.zwz.BTREE;
import java.util.Arrays;
/**
* B-tree
* 度 degree 节点的子节点数量
* 阶 order 所有节点中子节点最大值
*/
public class BTree {
static class Node {
//关键字
int[] keys;
//子节点
Node[] children;
//有效关键字数目
int keyNumber;
//是否是叶子节点
Boolean leaf = true;
//最小度数 孩子最少数量
int t;
/**
* 最小度数
* >=2
*
* @param t
*/
public Node(int t) {
this.t = t;
this.children = new Node[t << 1]; // 最多数量 t*2
this.keys = new int[(t << 1) - 1];
}
@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) {
break;
}
if (keys[i] == key) {
return this;
}
i++;
}
//叶子节点
if (leaf) {
return null;
} else {
//到子树查找
return children[i].get(key);
}
}
//向keys的指定索引index位置插入一个元素
//1 3 4 index = 1
// keys 1 keys 2 3
void insertKey(int key, int index) {
// for(int i = keyNumber-1;i>=index;i--){
// keys[i+1] = keys[i];
// }
System.arraycopy(keys, index, keys, index + 1, keyNumber - index);
keys[index] = key;
keyNumber++;
}
//向children 指定索引index 处插入children
void insertChildren(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 removeLeft() {
return removeKey(0);
}
/**
* 移除最右边的key
*/
int removeRight() {
return removeKey(keyNumber - 1);
}
/**
* 移除index处的child
*
* @param index
* @return
*/
Node removeChild(int index) {
Node t = children[index];
System.arraycopy(children, index + 1, children, index, --keyNumber - index);
return t;
}
/**
* 移除最左边的child
*/
Node removeLeftChild() {
return removeChild(0);
}
/**
* 移除最右边的child
*/
Node removeRightChild() {
return removeChild(keyNumber);
}
/**
* 获取孩子左边的兄弟
*/
Node childLeftSibling(int index) {
return index > 0 ? children[index - 1] : null;
}
/**
* 获取孩子右边的兄弟
*/
Node childRightSibling(int index) {
return index == keyNumber ? null : children[index + 1];
}
/**
* 赋值节点到target
*/
void moveToTarget(Node target) {
int start = target.keyNumber;
if (!leaf) {
for (int i = 0; i <= keyNumber; i++) {
target.children[start + i] = children[i];
}
}
for (int i = 0; i <= keyNumber; i++) {
target.keys[start + i] = keys[i];
}
}
}
//定根节点
Node root;
//最小度数
int t;
int MIN_KEY_NUMBER;//key最小数目
int MAX_KEY_NUMBER;//key最大数目
public BTree() {
this(2);
}
public BTree(int t) {
this.t = t;
root = new Node(2);
MAX_KEY_NUMBER = t * 2 - 1;
MIN_KEY_NUMBER = t - 1;
}
/**
* 判断是否存在
*/
public Boolean contains(int key) {
return root.get(key) != null;
}
/**
* 新增
* 1. 当前节点寻找空位,
* 2. 如果没找到空为
* 如果节点是叶子节点,直接插入
* 如果节点是非叶子节点,继续再children【】中查找
* 3. 根据MAX_KEY_NUMBER 判单是否进行分裂
*/
public void put(int key) {
doPut(root, key, null, 0);
}
/**
* put的递归方法
*/
public void doPut(Node node, int key, Node parent, int index) {
int i = 0;
//当前节点寻找位置
while (i < node.keyNumber) {
//找到节点,直接替换值
if (key == node.keys[i]) {
//执行替换值
//这个tree没有写value,所以直接return
return;
}
//找到空位,插入
if (node.keys[i] > key) {
break;
}
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);
}
}
/**
* 分裂
* 1.创建right节点(分裂后相当于left),把t以后的key和children都拷贝过去
* 2.t-1 处的key插入到parent的index处,index作为索引
* 3.right节点作为parent的孩子插入到index+1处
*/
private void split(Node left, Node parent, int index) {
//如果是根节点,则需要进行特殊处理
if (parent == null) {
Node newRoot = new Node(t);
//设置属性为非根节点
newRoot.leaf = false;
newRoot.insertChildren(left, 0);
this.root = newRoot;
parent = newRoot;
}
//1.创建right节点
Node right = new Node(t);
//2.创建的right节点和分裂的节点应该是同一层级
right.leaf = left.leaf;
//3.将left的t往后的节点拷贝到right中
System.arraycopy(left.keys, t, right.keys, 0, t - 1);
//3.1 如果是非叶子节点,则需要拷贝孩子
if (!left.leaf) {
System.arraycopy(left.children, t, right.children, 0, t);
}
//4.新节点有效数目更新,老节点有效数目更新
right.keyNumber = t - 1;
left.keyNumber = t - 1;
//5. 获取中间的key
int mid = left.keys[t - 1];
//6.中间节点插入到父节点
parent.insertKey(mid, index);
//7.right作为父节点的孩子
parent.insertChildren(right, index + 1);
}
/**
* 删除
* 删除节点中某个元素
*/
public void remove(int key) {
doRemove(null,root,0,key);
}
/**
* 执行删除的递归方法
*/
private void doRemove(Node panret,Node node,int index, int key) {
int i = 0;
while (i < node.keyNumber) {
//找到节点
if (node.keys[i] >= key) {
break;
}
i++;
}
//是否是叶子节点
if (node.leaf) {
//超出范围
if (i >= node.keyNumber) {
return;
} else {
//找到节点
if (node.keys[i] == key) {
//删除指定位置的key
node.removeKey(i);
} else {
//没找到
return;
}
}
} else {
//超出范围
//找最右侧的叶子节点
if (i >= node.keyNumber) {
doRemove(node,node.children[i],i, key);
} else {
//找到节点
if (node.keys[i] == key) {
//找到后继节点
//寻找右侧第一个节点
Node s = node.children[i + 1];
//不是叶子节点,继续向左找
while (!s.leaf) {
s = s.children[0];
}
//后继key
int sKey = s.keys[0];
//找到索引i处的key 进行替换
node.keys[i] = sKey;
//删除找到的这个后继key
doRemove(node,node.children[i + 1],i+1,sKey);
//直接删除也可以
//s.removeKey(0);
} else {
//没找到
//找对应索引位置的叶子节点
doRemove(node,node.children[i],i, key);
}
}
}
//调整平衡
if (node.keyNumber < MIN_KEY_NUMBER) {
balance(panret,node,i);
}
}
/**
* 平衡调整
*
* @param parent 待调整节点父节点
* @param x 待调整节点
* @param i 待调整节点索引
*/
private void balance(Node parent, Node x, int i) {
//待调整节点为根节点,特殊处理
if (x == root) {
if(root.keyNumber == 0 && root.children[0] != null){
root = root.children[0];
}
return;
}
Node leftSibling = parent.childLeftSibling(i);//左兄弟
Node rightSibling = parent.childRightSibling(i);//右兄弟
//如果左兄弟数量足够,则右旋
if (leftSibling != null && leftSibling.keyNumber > MIN_KEY_NUMBER) {
//1.取父节点i-1索引的key 插入到x的索引0位置中
x.insertKey(parent.keys[i - 1], 0);
//2.取左兄弟的最右侧key,插入到父节点i-1位置中
parent.keys[i - 1] = leftSibling.removeRight();
//3.不是叶子节点 处理child,
if (!leftSibling.leaf) {
//将最右侧的孩子,放入x的索引0位置
x.insertChildren(leftSibling.removeRightChild(), 0);
}
return;
}
//如果右兄弟数量足够,则左旋
if (rightSibling != null && rightSibling.keyNumber > MIN_KEY_NUMBER) {
//1.取父节点i索引的key 插入到x的索引0位置中
x.insertKey(parent.keys[i], x.keyNumber);
//2.取右兄弟的最左侧key,插入到父节点i位置中
parent.keys[i] = rightSibling.removeLeft();
//3.不是叶子节点 处理child,
if (!rightSibling.leaf) {
//将最左侧的孩子,放入x的索引x.keyNumber+1位置
x.insertChildren(rightSibling.removeLeftChild(), x.keyNumber+1);
}
return;
}
//如果左右数量都不够,则合并
if(leftSibling != null){
//左兄弟不为空,自身向左合并
//1.将parent索引i位置的child删除
parent.removeChild(i);
//2.将parent i-1处的key,移动到左兄弟最右侧
leftSibling.insertKey(parent.removeKey(i-1), leftSibling.keyNumber);
//3.x移动到左兄弟处
x.moveToTarget(leftSibling);
}else{
//右兄弟不为空,向自身合并
//1.将parent索引i+1位置的child删除
parent.removeChild(i+1);
//2.将parent i处的key,移动到自身最右侧
x.insertKey(parent.removeKey(i), x.keyNumber);
//3.右兄弟移动到x
rightSibling.moveToTarget(x);
}
}
}
数据结构和算法-bTRee
最新推荐文章于 2024-11-14 16:38:00 发布