一、AVL树
1.AVL的定义和特征
特征:搜索树&&平衡树
搜索树:任取结点,其左孩子.key<其.key<右孩子.key,因此中序遍历是有序的,按照key值顺序大小排列
平衡树:结点左子树的高度和结点右子树的高度的高度差的绝对值不超过1
平衡因子:bf(node)=H(node.left)-H(node.right)
2.AVL的操作步骤
①查找
如果 key==current.key: 查找到了key,current就是包含了key的结点
如果 key<current.key: current=current.left
如果 key>current.key: current=current.right
如果current == null :key不在集合中
时间复杂度~O(树的高度) 即O(log(n))
②插入
1.按照普通搜索树的插入规则进行结点的插入
2.随着插入的完成,导致平衡因子出现的变化,所以要进行平衡因子的调节
BF(node)==0
BF(parent.parent) 不变
BF(parent)
-
if(node是parent.left) 则 H(parent.left)++ 即 BF(parent)++
-
else(node是parent.right) 则 H(parent.right)++ 即 BF(parent)–
失衡情况:
LL:对parent右旋,插入结束
LR:对node左旋,对parent右旋,插入结束
RR:对parent左旋,插入结束
RR:对node右旋,对parent左旋,插入结束
3.AVL树的插入过程正确性的证明
4.AVL树的实现
结点
public class AVLNode {
public long key;
public AVLNode left;
public AVLNode right;
//特殊字段
public int bf; //平衡因子
public AVLNode parent; //父结点
@Override
public String toString() {
return "AVLNode{" +
"key=" + key +
", bf=" + bf +
'}';
}
}
AVL树
public class AVLTree {
public AVLNode root = null;
public int size = 0; // 保存树的结点个数
public void insert(long key) {
AVLNode node = new AVLNode();
node.key = key;
node.left = null;
node.right = null;
node.parent = null;
node.bf = 0;
if (root == null) {
root = node;
size++;
return;
}
AVLNode current = root;
AVLNode parent = null;
while (current != null) {
if (key == current.key) {
return;
//throw new RuntimeException("插入失败,key 有重复: " + key);
} else if (key < current.key) {
parent = current;
current = current.left;
} else {
parent = current;
current = current.right;
}
}
node.parent = parent;
if (key < parent.key) {
parent.left = node;
} else {
parent.right = node;
}
avlAdjust(parent, node);
size++;
}
private void avlAdjust(AVLNode parent, AVLNode node) {
// parent != null && node != null
while (true) {
// 进行平衡因子的调整
if (node == parent.left) {
parent.bf++;
} else {
parent.bf--;
}
// 第一种情况
if (parent.bf == 0) {
return;
}
// 第二种情况
if (parent.bf == -1 || parent.bf == 1) {
node = parent;
parent = parent.parent;
if (parent == null) {
// 向上回溯到根的位置了
return;
}
continue;
}
// 情况三
// parent.bf == -2 || parent.bf == 2
break;
}
// 一定是出现失衡情况了
if (parent.bf == 2) {
if (node.bf == 1) {
// LL 失衡
rightRotate(parent);
parent.bf = node.bf = 0;
} else {
// LR 失衡
// node.bf == -1
AVLNode c = node.right;
int condition;
if (parent.right == null) {
condition = 1;
} else if (c.bf == 1) {
condition = 2;
} else {
condition = 3;
}
leftRotate(node);
rightRotate(parent);
if (condition == 1) {
parent.bf = node.bf = c.bf = 0;
} else if (condition == 2) {
parent.bf = -1;
node.bf = c.bf = 0;
} else {
parent.bf = c.bf = 0;
node.bf = 1;
}
}
} else {
// parent.bf == -2
if (node.bf == -1) {
// RR 失衡
leftRotate(parent);
parent.bf = node.bf = 0;
} else {
// RL 失衡
// node.bf == 1
AVLNode c = node.left;
int condition;
if (parent.left == null) {
condition = 1;
} else if (c.bf == 1) {
condition = 2;
} else {
condition = 3;
}
rightRotate(node);
leftRotate(parent);
if (condition == 1) {
parent.bf = node.bf = 0;
} else if (condition == 2) {
parent.bf = c.bf = 0;
node.bf = -1;
} else {
parent.bf = 1;
node.bf = c.bf = 0;
}
}
}
}
// 以 m 为结点,进行旋转
private void leftRotate(AVLNode m) {
// m 代表图中的 b 结点
// parent 代表 b 结点可能存在的父亲
AVLNode parent = m.parent;
// right 代表图中的 a 结点
AVLNode right = m.right;
// leftOfRight 代表图中的可能存在的乙子树的根结点
AVLNode leftOfRight = right.left;
/*
其中: m != null && right != null
但是: parent 不保证 !null, leftOfRight 不保证 !null
*/
right.parent = parent; // 蓝色线的关系
// 黑色线的关系
if (parent == null) {
// m 是 root
root = right;
} else {
if (m == parent.left) {
parent.left = right;
} else {
parent.right = right;
}
}
right.left = m; // 黑色线的关系
m.parent = right; // 蓝色线的关系
m.right = leftOfRight;
if (leftOfRight != null) {
leftOfRight.parent = m;
}
}
private void rightRotate(AVLNode m) {
AVLNode parent = m.parent;
AVLNode left = m.left;
AVLNode rightOfLeft = left.right;
left.parent = parent;
if (parent == null) {
root = left;
} else {
if (m == parent.left) {
parent.left = left;
} else {
parent.right = left;
}
}
left.right = m;
m.parent = left;
m.left = rightOfLeft;
if (rightOfLeft != null) {
rightOfLeft.parent = m;
}
}
public static void main(String[] args) {
AVLTree tree = new AVLTree();
AVLNode n5 = new AVLNode();
n5.key = 5;
AVLNode n6 = new AVLNode();
n6.key = 6;
AVLNode n7 = new AVLNode();
n7.key = 7;
n5.right = n6;
n6.parent = n5;
n6.right = n7;
n7.parent = n6;
tree.root = n5;
tree.leftRotate(tree.root);
System.out.println("Hello");
}
}
二、红黑树
1.红黑树的定义
红黑树是一种“平衡”二叉搜索树
“平衡”:相比较于AVL树来说,是一种弱平衡
在红黑树中,任意从根到叶子的路径中,LEN(最长的路径)<=2*LEN(最短的路径)
详细规则:
- 红黑树中的结点:或红或黑
- 红黑树的根一定是黑色的
- 红黑树的“叶子结点”一定是黑色的
注:平时我们理解的叶子:node.left == null && node.right == null,而此处的叶子:node==null - 红黑树中,任意路径(从根到叶子),红色不能和红色相邻
- 红黑树中,任意路径(从根到叶子),黑色结点的数量必须一样多
查找操作:同普通的搜索树规则一样
插入操作:
- 按照普通搜索树的方式插入,新插入的结点一定是红色
- 判断是否破坏了红色和红色不能相邻的规则,如果破坏了需进行平衡调整操作
- 将根置为黑色
2.平衡调整规则
① uncle存在 && uncle的颜色是红色
- parent的颜色=黑色
- uncle的颜色=黑色
- grandpa的颜色=红色
- 把grandpa视为node,一直回溯到根结点(回溯退出的情况:回溯到根上、回溯到没破坏红色不能相邻)
②如果关系不一致 - 旋转祖父
- 祖父的颜色改成红色
- 父亲的颜色改成黑色
2.实现
结点
public class RBTreeNode {
public static final boolean BLACK=true;
public static final boolean RED=false;
public long key;
public RBTreeNode left;
public RBTreeNode right;
public RBTreeNode parent;
public boolean color;
public RBTreeNode(long key, RBTreeNode parent, boolean color) {
this.key = key;
this.parent = parent;
this.color = color;
}
@Override
public String toString() {
return "RBTreeNode{" +
"key=" + key +
", color=" + color +
'}';
}
}
红黑树
/**
* 红黑树
*/
public class RBTree {
/**
* 保存红黑树的根结点
*/
public RBTreeNode root = null;
/**
* 保存红黑树中的结点个数
*/
public int size = 0;
/**
* 向红黑树中插入新的关键字
* @param key 关键字
* @return 是否插入成功
* true: 插入成功
* false: 插入失败(key 出现重复)
*/
public boolean insert(long key) {
// 1. 按照普通搜索树的方式进行插入
if (root == null) {
root = new RBTreeNode(key, null, BLACK);
size++;
return true;
}
RBTreeNode parent = null;
RBTreeNode current = root;
while (current != null) {
if (key == current.key) {
return false;
} else if (key < current.key) {
parent = current;
current = current.left;
} else {
parent = current;
current = current.right;
}
}
/**
* 根据插入规则,每次新插入的结点,一定是红色的
*/
RBTreeNode node = new RBTreeNode(key, parent, RED);
if (key < parent.key) {
parent.left = node;
} else {
parent.right = node;
}
size++;
/**
* 进行红黑树规则的判断 + 平衡调整过程
*/
adjustBalance(node);
return true;
}
private void adjustBalance(RBTreeNode node) {
while (true) {
RBTreeNode parent = node.parent;
if (parent == null) {
break;
}
if (parent.color == BLACK) {
break;
}
/**
* 一定破坏了"红色不能相邻"的规则
*/
RBTreeNode grandpa = parent.parent;
// 找到叔叔结点
if (parent == grandpa.left) {
RBTreeNode uncle = grandpa.right;
if (uncle != null && uncle.color == RED) {
/**
* 情况1:叔叔存在 并且 叔叔的颜色是红色
* 步骤:
* 1. 叔叔和父亲的颜色改成黑色
* 2. 祖父的颜色改成红色
* 3. 把祖父视为 node,再去判断是否违反规则了
*/
parent.color = uncle.color = BLACK;
grandpa.color = RED;
node = grandpa;
continue;
} else {
// 判断 grandpa <-> parent 和 parent <-> node 的关系是否不一致
// 已知 parent 是 grandpa 的左边
if (node == parent.right) {
leftRotate(parent);
//swap(parent, node);
parent = node;
}
// 接下来统一处理关系一致的情况
rightRotate(grandpa);
grandpa.color = RED;
parent.color = BLACK;
break;
}
} else {
RBTreeNode uncle = grandpa.left;
if (uncle != null && uncle.color == RED) {
/**
* 情况1:叔叔存在 并且 叔叔的颜色是红色
* 步骤:
* 1. 叔叔和父亲的颜色改成黑色
* 2. 祖父的颜色改成红色
* 3. 把祖父视为 node,再去判断是否违反规则了
*/
parent.color = uncle.color = BLACK;
grandpa.color = RED;
node = grandpa;
continue;
} else {
// 判断 grandpa <-> parent 和 parent <-> node 的关系是否不一致
// 已知 parent 是 grandpa 的右边
if (node == parent.left) {
rightRotate(parent);
//swap(parent, node);
parent = node;
}
// 接下来统一处理关系一致的情况
leftRotate(grandpa);
grandpa.color = RED;
parent.color = BLACK;
break;
}
}
}
/**
* 无论之前是什么情况,统一把根改成黑色
* 走到此处时,root 一定不是 null
*/
root.color = BLACK;
}
// 以 m 为结点,进行旋转
private void leftRotate(RBTreeNode m) {
// m 代表图中的 b 结点
// parent 代表 b 结点可能存在的父亲
RBTreeNode parent = m.parent;
// right 代表图中的 a 结点
RBTreeNode right = m.right;
// leftOfRight 代表图中的可能存在的乙子树的根结点
RBTreeNode leftOfRight = right.left;
/*
其中: m != null && right != null
但是: parent 不保证 !null, leftOfRight 不保证 !null
*/
right.parent = parent; // 蓝色线的关系
// 黑色线的关系
if (parent == null) {
// m 是 root
root = right;
} else {
if (m == parent.left) {
parent.left = right;
} else {
parent.right = right;
}
}
right.left = m; // 黑色线的关系
m.parent = right; // 蓝色线的关系
m.right = leftOfRight;
if (leftOfRight != null) {
leftOfRight.parent = m;
}
}
private void rightRotate(RBTreeNode m) {
RBTreeNode parent = m.parent;
RBTreeNode left = m.left;
RBTreeNode rightOfLeft = left.right;
left.parent = parent;
if (parent == null) {
root = left;
} else {
if (m == parent.left) {
parent.left = left;
} else {
parent.right = left;
}
}
left.right = m;
m.parent = left;
m.left = rightOfLeft;
if (rightOfLeft != null) {
rightOfLeft.parent = m;
}
}
}
三、B-树系列
1.定义
B-树是一棵多叉、平衡搜索树
B-树中每一个结点在实现时人为规定一个key的上限
B-树结点中的key时按照key的顺序进行保存
我们把key的个数记为size,则child的个数一定是size+1
2.插入规则
- 所有插入过程,只能发生在叶子上,不能发生在非叶子结点上
- 插入时,每个结点的key是有上限的
完成插入后:未达到上限则插入结束,否则进行结点分裂过程 - 关于结点分裂过程
- 找到key的中间值,把下标记为index,不一定是新插入的结点,偶数的话,前一个或者后一个都可以
- 把当前结点分成两个结点,【0,index)key留在当前结点当中,【index】提到父结点当中,(index,size)搬到新结点中
3.代码实现
BTreeNode
public class BTreeNode {
/**
* 先规定,一个 B-树结点中,能存的 key 的数量的上限,要求 size(key) <= KEY_LIMIT
* 在实际的应用中,一般 KEY_LIMIT 都是 1000 数量级以上的
*/
public static final int KEY_LIMIT = 4;
/**
* 去保存结点中所有的 key
* 使用数组(线性结构)进行维护,需要维护 key 与 key 之间的有序性
* 从空间利用率角度考虑,new long[KEY_LIMIT] 就足够了
* 但我们多申请一个空间,方便进行结点分裂代码的编写(用于保存即将分裂状态下的所有 key)
*/
public long[] keyList = new long[KEY_LIMIT + 1];
/**
* 记录目前拥有的 key 的个数
*/
public int size = 0;
/**
* 去维护结点中所有的 child
* 同理,多申请一个空间
* 本来,child 就比 key 多一个
* new BTreeNode[KEY_LIMIT + 2]
*/
public BTreeNode[] childList = new BTreeNode[KEY_LIMIT + 2];
/**
* 我们不维护 child 的个数,因为 其个数一定是 size + 1
*/
/**
* 保存该结点的父结点,null 代表该结点是根结点
*/
public BTreeNode parent = null;
@Override
public String toString() {
return String.format("%d: %s", size, Arrays.toString(Arrays.copyOf(keyList, size)));
}
/**
* 查找 key 位于当前结点的哪个子树中
* @param key 关键字
* @return
* this: key 位于当前结点
* 其他: 位于孩子结点中,可能是 null
*/
public BTreeNode findKey(long key) {
if (key > keyList[size - 1]) {
// child 比 key 多一个
// keyList 的最后一个元素位于 [size - 1]
// childList 的最后一个元素位于 [size]
return childList[size];
}
for (int i = 0; i < size; i++) {
if (key == keyList[i]) {
// key 就在当前结点中
return this;
} else if (key < keyList[i]) {
// 由于我们是从左向右遍历的
// 所以 keyList[i] 中的元素是 "第一个" 大于 key 的关键字元素
return childList[i];
} else {
// key > keyList[i] 的情况下,我们什么都不需要做,继续遍历查找
// 第一个 大于 key 的元素
}
}
// 我们的代码组织方式使得,理论上,不应该走到这个位置
// 但 java 编译器分析不出来,所以,为了让编译器不报错,我们随便返回一个值
// 这个 return 没有任何执行的意义
return null;
}
/**
* 用来作为插入的返回值类型的类
*/
public static class InsertResult {
public long key; // 分裂出的 key
public BTreeNode node; // 分裂出的新结点
}
/**
* 将指定关键字 key,插入叶子结点中
* @param key 关键字
* @return
* null: 插入之后不需要分裂
* !null: 发生了分裂
*/
public InsertResult insertKey(long key) {
// 1. 如果 key 按序插入到 keyList 中
insertIntoKeyList(key);
size++;
// 2. 判断是否需要进行分裂
// 3. 如果有必要,进行结点分裂
if (shouldSplit()) {
return splitLeaf();
}
return null;
}
/**
* 将指定关键字 key 和 chid,插入非叶子结点中
* @param key 关键字
* @param child > key 的孩子
* @return
*/
public InsertResult insertKeyWithChild(long key, BTreeNode child) {
// 1. 如果 key 按序插入到 keyList 中
int index = insertIntoKeyList(key);
// 2. 根据 index 进行 child 的插入,暂时没有进行 size 的变动
// child[0, size]
// [0, index] 不动
// [index + 1] 要插入 child 的下标
// [index + 1, size] 搬到 [index + 2, size + 1] 元素个数 size - (index + 1) + 1 == size - index
System.arraycopy(childList, index + 1, childList, index + 2, size - index);
childList[index + 1] = child;
// 这个时候再让 size++
size++;
// 2. 判断是否需要进行分裂
// 3. 如果有必要,进行结点分裂
if (shouldSplit()) {
return splitNotLeaf();
}
return null;
}
public InsertResult splitNotLeaf() {
// 因为一会儿 this.size 在中途会变化,先提前记录
int size = this.size;
InsertResult result = new InsertResult();
BTreeNode node = new BTreeNode();
// 1. 找到 keyList 的中间位置
int index = size / 2;
// 2. 保存需要分裂出的 key
result.key = keyList[index];
/**
* 3. 处理 key 的分裂
* 哪些 key 应该留在当前结点中 [0, index) 一共有 index 个
* 哪个 key 是分裂出的 key [index]
* 哪些 key 应该在新结点中 (index, size) 一共有 size - index - 1
*/
// 先把 (index, size) 位置的 key 搬到新结点中
System.arraycopy(keyList, index + 1, node.keyList, 0, size - index - 1);
node.size = size - index - 1;
// 把 [index, size) 所有 key 重置为默认值 (0),重置 size
Arrays.fill(keyList, index, size, 0);
this.size = index;
/**
* 4. 我们对非叶子结点进行分裂,非叶子结点有孩子,所以也进行分裂
*/
System.arraycopy(this.childList, index + 1, node.childList, 0, size - index);
Arrays.fill(this.childList, index + 1, size + 1, null);
for (int i = 0; i < size - index; i++) {
node.childList[i].parent = node;
}
/**
* 5. 处理分裂的 parent 的问题
* 1) this.parent 不变,分裂不影响父子关系
* 2) node.parent 和 this 是同一个父亲
*/
node.parent = this.parent;
result.node = node;
return result;
}
public InsertResult splitLeaf() {
// 因为一会儿 this.size 在中途会变化,先提前记录
int size = this.size;
InsertResult result = new InsertResult();
BTreeNode node = new BTreeNode();
// 1. 找到 keyList 的中间位置
int index = size / 2;
// 2. 保存需要分裂出的 key
result.key = keyList[index];
/**
* 3. 处理 key 的分裂
* 哪些 key 应该留在当前结点中 [0, index) 一共有 index 个
* 哪个 key 是分裂出的 key [index]
* 哪些 key 应该在新结点中 (index, size) 一共有 size - index - 1
*/
// 先把 (index, size) 位置的 key 搬到新结点中
System.arraycopy(keyList, index + 1, node.keyList, 0, size - index - 1);
node.size = size - index - 1;
// 把 [index, size) 所有 key 重置为默认值 (0),重置 size
Arrays.fill(keyList, index, size, 0);
this.size = index;
/**
* 4. 我们对叶子结点进行分裂,叶子结点没有孩子 childList[...] = null,不需要分裂
*/
/**
* 5. 处理分裂的 parent 的问题
* 1) this.parent 不变,分裂不影响父子关系
* 2) node.parent 和 this 是同一个父亲
*/
node.parent = this.parent;
result.node = node;
return result;
}
public boolean shouldSplit() {
return size > KEY_LIMIT;
}
public int insertIntoKeyList(long key) {
// keyList 中有效的下标范围是 [0, size)
// 从后向前遍历 [size - 1, 0]
int i;
for (i = size - 1; i >= 0; i--) {
if (keyList[i] < key) {
// 第一次碰到了 keyList[i] < key
break;
}
// 没有 keyList[i] == key
// 继续遍历查找,同时将数据向后搬一格
keyList[i + 1] = keyList[i];
}
// 1. i == -1
// 2. keyList[i] < key
// 不管怎么样,key 都应该放在 [i + 1]
keyList[i + 1] = key;
return i + 1;
}
public static void testFindKey() {
BTreeNode node = new BTreeNode();
node.keyList[0] = 10;
node.keyList[1] = 20;
node.keyList[2] = 30;
node.keyList[3] = 40;
node.size = 4;
BTreeNode 甲 = new BTreeNode();
BTreeNode 乙 = new BTreeNode();
BTreeNode 丙 = new BTreeNode();
BTreeNode 丁 = new BTreeNode();
BTreeNode 戊 = new BTreeNode();
node.childList[0] = 甲;
node.childList[1] = 乙;
node.childList[2] = 丙;
node.childList[3] = 丁;
node.childList[4] = 戊;
/**
* 测试用例:
* 执行动作 期望结果
* < 10 期望 甲
* == 10 期望 node
* == 20 期望 node
* (10, 20) 期望 乙
* (20, 30) 期望 丙
* (30, 40) 期望 丁
* > 40 期望 戊
* == 40 期望 node
*/
// 引用1 == 引用2: 判断两个引用是否指向同一个对象
System.out.println(node.findKey(3) == 甲);
System.out.println(node.findKey(10) == node);
System.out.println(node.findKey(20) == node);
System.out.println(node.findKey(13) == 乙);
System.out.println(node.findKey(29) == 丙);
System.out.println(node.findKey(31) == 丁);
System.out.println(node.findKey(300) == 戊);
System.out.println(node.findKey(40) == node);
}
public static void testInsertKey() {
BTreeNode parent = new BTreeNode();
BTreeNode node = new BTreeNode();
node.parent = parent;
// 第一个测试用例
// 理论上这个不应该测,因为 B-树的插入过程中从不会有往一个空的(不是 null)结点中插入的
// B-树的结点都是分裂出来的,不是手动 new 出来的
// B-树一定包含从原来的结点中分出来的 key
InsertResult r = node.insertKey(15);
System.out.println(r == null);
System.out.println(node.size == 1);
System.out.println(node.keyList[0] == 15);
System.out.println(node.parent == parent);
// 测试用例 2
r = node.insertKey(7);
System.out.println(r == null);
System.out.println(node.size == 2);
System.out.println(node.keyList[0] == 7);
System.out.println(node.keyList[1] == 15);
System.out.println(node.parent == parent);
// 测试用例 3
r = node.insertKey(19);
System.out.println(r == null);
System.out.println(node.size == 3);
System.out.println(node.keyList[0] == 7);
System.out.println(node.keyList[1] == 15);
System.out.println(node.keyList[2] == 19);
System.out.println(node.parent == parent);
// 测试用例 4
r = node.insertKey(10);
System.out.println(r == null);
System.out.println(node.size == 4);
System.out.println(node.keyList[0] == 7);
System.out.println(node.keyList[1] == 10);
System.out.println(node.keyList[2] == 15);
System.out.println(node.keyList[3] == 19);
System.out.println(node.parent == parent);
// 测试用例 5
r = node.insertKey(17);
System.out.println(r != null);
System.out.println(node.size == 2);
System.out.println(node.keyList[0] == 7);
System.out.println(node.keyList[1] == 10);
System.out.println(node.keyList[2] == 0);
System.out.println(node.keyList[3] == 0);
System.out.println(node.keyList[4] == 0);
System.out.println(node.parent == parent);
System.out.println(r.key == 15);
System.out.println(r.node.size == 2);
System.out.println(r.node.keyList[0] == 17);
System.out.println(r.node.keyList[1] == 19);
System.out.println(r.node.keyList[2] == 0);
System.out.println(r.node.keyList[3] == 0);
System.out.println(r.node.keyList[4] == 0);
System.out.println(r.node.parent == parent);
}
public static void main(String[] args) {
//testFindKey();
testInsertKey();
}
}
BTree
/**
* B-树定义
*/
public class BTree {
/**
* B-树的根结点
*/
public BTreeNode root = null;
/**
* B-树中的 key 的个数
* 备注:和二叉搜索树不同,B-树的 key 的个数 不等于 结点的个数
*/
public int size = 0;
public boolean insert(long key) {
if (insertWithoutSize(key)) {
size++;
return true;
}
return false;
}
public boolean insertWithoutSize(long key) {
// 1. 是否是空树
if (root == null) {
// root = 构建一个包含 key 的结点;
root = new BTreeNode();
root.keyList[0] = key;
root.size = 1;
return true;
}
// 2. 不是空树的情况
BTreeNode current = root;
while (true) {
BTreeNode node = current.findKey(key);
if (node == current) {
// key 就在 current 结点中
// 不运行重复 key 的插入,插入失败
return false;
} else if (node == null) {
// current 就是叶子结点
// current 就是接下来要插入 key 的结点
break;
} else {
// 找到了一个孩子 并且 current 不是叶子
// 规则:插入必须发生在叶子结点上
current = node;
}
}
// 进行 key 的插入
// current 就是我们要进行 key 插入的结点
// current 此时一定是叶子
InsertResult r = current.insertKey(key);
while (true) {
if (r == null) {
// 插入过程中,没有发生结点分裂,直接退出
return true;
}
// 说明发生分裂了
// 需要把新分裂出的 key 和 分裂出的结点,插入到 current.parent 中
BTreeNode parent = current.parent;
if (parent == null) {
// 说明 current 是根结点
// 根结点发生分裂了
// 需要一个新的根结点,来保存分裂出的 key
root = new BTreeNode();
root.keyList[0] = r.key;
root.size = 1;
root.childList[0] = current;
root.childList[1] = r.node;
// 由于原来 current 是根结点,所以其 parent == null
// 自然分裂出的结点,跟着也是 null
// 所以为他们设置新的父结点
current.parent = r.node.parent = root;
return true;
}
// 不是根结点分裂了
r = parent.insertKeyWithChild(r.key, r.node);
current = parent;
}
}
}