diff --git a/BinaryTree/BalanceTree/B-Tree/README.MD b/BinaryTree/BalanceTree/B-Tree/README.MD index 7be146c..d562f4d 100644 --- a/BinaryTree/BalanceTree/B-Tree/README.MD +++ b/BinaryTree/BalanceTree/B-Tree/README.MD @@ -569,6 +569,142 @@ deleteKey(node, k): --- +✅ 对的,你理解完全正确!我帮你梳理一下: + +--- + +### 在 B-树删除时,如果要删除的关键字 `x->keys[i]` 在**内部节点**: + +* **前驱(predecessor)** + + * 就是 `x->children[i]` 这个子树中的最大值。 + * 为什么?因为 `x->children[i]` 里所有关键字都 **小于** `x->keys[i]`,而前驱就是其中最大的。 + * 找法:一路往右孩子走直到叶子,拿到最后一个 key。 + * 伪代码: + + ```cpp + Node* cur = x->children[i]; + while (!cur->isLeaf) cur = cur->children.back(); + T predecessor = cur->keys.back(); + ``` + +* **后继(successor)** + + * 就是 `x->children[i+1]` 这个子树中的最小值。 + * 为什么?因为 `x->children[i+1]` 里所有关键字都 **大于** `x->keys[i]`,而后继就是其中最小的。 + * 找法:一路往左孩子走直到叶子,拿到第一个 key。 + * 伪代码: + + ```cpp + Node* cur = x->children[i+1]; + while (!cur->isLeaf) cur = cur->children.front(); + T successor = cur->keys.front(); + ``` + +--- + +### 删除时的策略 + +* 如果前驱所在子树 `x->children[i]` 有 ≥ t 个 key: + + * 用前驱替代 `x->keys[i]`,再递归删除前驱。 +* 否则如果后继所在子树 `x->children[i+1]` 有 ≥ t 个 key: + + * 用后继替代 `x->keys[i]`,再递归删除后继。 +* 否则(两个子树都只有 t-1 个 key): + + * 把 `x->keys[i]` 下移到左子树,和右子树合并,再递归删除。 + +--- + +📘 **直观理解**: +`x->keys[i]` 在父节点,它左边整个子树都比它小,右边整个子树都比它大。 +所以前驱就是左子树里最大值,后继就是右子树里最小值。 + +--- + +要不要我帮你画一个 **图解例子**(比如 `t=2` 的 B 树,删除一个内部节点的情况),演示「前驱替代」和「后继替代」的区别? + + 如果你需要,我可以帮你 **写一个完整的 C++ B 树删除函数实现**,直接和你已有的 `BTree` 类整合,并附详细注释。 你希望我帮你写吗? + +--- +``` html +总结: +1、删除 +2、查看是否下溢出 +3、下溢出: + a.左右节点可以借(大于t - 1) + 父亲下来,借的成为父亲 + b.不可以借 + 和兄弟合并 + 总结得很到位 👍,我帮你整理一下,更系统一些,方便你以后复习 B-树删除算法。 +``` +--- + +## 🌳 B-树删除算法总结 + +### 1. 删除目标 + +* **在叶子节点找到 key** + 👉 直接删除,不涉及复杂调整。 + +* **在内部节点找到 key** + 👉 不能直接删,而是通过 **前驱 / 后继替代** 或 **合并** 来保持 B-树平衡。 + +--- + +### 2. 删除步骤总览 + +1. **删除目标 key** + + * 在叶子:直接删。 + * 在内部:找前驱或后继替换,再递归删除。 + +2. **检查是否下溢(underflow)** + + * B-树的每个节点至少要有 `t - 1` 个 key(除了根)。 + * 如果某个子节点删除后不足 `t - 1`,则发生下溢。 + +3. **处理下溢** + + * **a. 向兄弟借关键字(兄弟 ≥ t)** + + * 父节点“下放”一个关键字到下溢的节点,兄弟的关键字“上升”到父节点。 + * 保证节点恢复到 ≥ `t - 1`。 + * 📌 这是 **旋转(rotation)** 操作。 + + * **b. 无法借(兄弟也是 t - 1) → 合并** + + * 将下溢节点、父节点中一个关键字、兄弟节点 **合并** 成一个节点。 + * 父节点关键字数减少 1。 + * 如果父节点变空,整棵树高度减 1(根被替换)。 + +--- + +### 3. 前驱 / 后继选择 + +* **前驱**:`x->keys[i]` 的左子树 `x->children[i]` 中的最大值。 +* **后继**:`x->keys[i]` 的右子树 `x->children[i+1]` 中的最小值。 +* 替代规则: + + * 左子树有足够关键字(≥ t),用前驱替代。 + * 否则若右子树有足够关键字(≥ t),用后继替代。 + * 否则左右子树都不够,执行合并。 + +--- + +### 📌 最简总结版(你说的很对) + +1. 删除目标 key。 +2. 如果节点关键字数 ≥ `t - 1`,不用处理。 +3. 如果 < `t - 1`(下溢): + + * **能借** → 借兄弟。 + * **不能借** → 合并兄弟和父节点关键字。 + +--- + +要不要我帮你画一个小例子(`t=2` 的 B-树),演示 **借** 和 **合并** 两种下溢处理,这样你一眼就能看懂? diff --git a/BinaryTree/BalanceTree/B-Tree/img/erase/1.png b/BinaryTree/BalanceTree/B-Tree/img/erase/1.png new file mode 100644 index 0000000..4935f44 Binary files /dev/null and b/BinaryTree/BalanceTree/B-Tree/img/erase/1.png differ diff --git a/BinaryTree/BalanceTree/B-Tree/img/erase/2.png b/BinaryTree/BalanceTree/B-Tree/img/erase/2.png new file mode 100644 index 0000000..6ed39b5 Binary files /dev/null and b/BinaryTree/BalanceTree/B-Tree/img/erase/2.png differ diff --git a/BinaryTree/BalanceTree/B-Tree/img/erase/3.png b/BinaryTree/BalanceTree/B-Tree/img/erase/3.png new file mode 100644 index 0000000..29db8c9 Binary files /dev/null and b/BinaryTree/BalanceTree/B-Tree/img/erase/3.png differ diff --git a/BinaryTree/BalanceTree/B-Tree/img/erase/4.png b/BinaryTree/BalanceTree/B-Tree/img/erase/4.png new file mode 100644 index 0000000..1c5a8cd Binary files /dev/null and b/BinaryTree/BalanceTree/B-Tree/img/erase/4.png differ diff --git a/BinaryTree/BalanceTree/B-Tree/img/erase/5.png b/BinaryTree/BalanceTree/B-Tree/img/erase/5.png new file mode 100644 index 0000000..14d29a6 Binary files /dev/null and b/BinaryTree/BalanceTree/B-Tree/img/erase/5.png differ diff --git a/BinaryTree/BalanceTree/B-Tree/img/erase/6.png b/BinaryTree/BalanceTree/B-Tree/img/erase/6.png new file mode 100644 index 0000000..3956fcf Binary files /dev/null and b/BinaryTree/BalanceTree/B-Tree/img/erase/6.png differ diff --git a/BinaryTree/BalanceTree/B-Tree/img/insert/1.png b/BinaryTree/BalanceTree/B-Tree/img/insert/1.png new file mode 100644 index 0000000..2006ef4 Binary files /dev/null and b/BinaryTree/BalanceTree/B-Tree/img/insert/1.png differ diff --git a/BinaryTree/BalanceTree/B-Tree/img/insert/10.png b/BinaryTree/BalanceTree/B-Tree/img/insert/10.png new file mode 100644 index 0000000..4934df5 Binary files /dev/null and b/BinaryTree/BalanceTree/B-Tree/img/insert/10.png differ diff --git a/BinaryTree/BalanceTree/B-Tree/img/insert/2.png b/BinaryTree/BalanceTree/B-Tree/img/insert/2.png new file mode 100644 index 0000000..0861394 Binary files /dev/null and b/BinaryTree/BalanceTree/B-Tree/img/insert/2.png differ diff --git a/BinaryTree/BalanceTree/B-Tree/img/insert/3.png b/BinaryTree/BalanceTree/B-Tree/img/insert/3.png new file mode 100644 index 0000000..9c18803 Binary files /dev/null and b/BinaryTree/BalanceTree/B-Tree/img/insert/3.png differ diff --git a/BinaryTree/BalanceTree/B-Tree/img/insert/4.png b/BinaryTree/BalanceTree/B-Tree/img/insert/4.png new file mode 100644 index 0000000..5213906 Binary files /dev/null and b/BinaryTree/BalanceTree/B-Tree/img/insert/4.png differ diff --git a/BinaryTree/BalanceTree/B-Tree/img/insert/5.png b/BinaryTree/BalanceTree/B-Tree/img/insert/5.png new file mode 100644 index 0000000..0a4f833 Binary files /dev/null and b/BinaryTree/BalanceTree/B-Tree/img/insert/5.png differ diff --git a/BinaryTree/BalanceTree/B-Tree/img/insert/6.png b/BinaryTree/BalanceTree/B-Tree/img/insert/6.png new file mode 100644 index 0000000..590b31c Binary files /dev/null and b/BinaryTree/BalanceTree/B-Tree/img/insert/6.png differ diff --git a/BinaryTree/BalanceTree/B-Tree/img/insert/7.png b/BinaryTree/BalanceTree/B-Tree/img/insert/7.png new file mode 100644 index 0000000..f4b1712 Binary files /dev/null and b/BinaryTree/BalanceTree/B-Tree/img/insert/7.png differ diff --git a/BinaryTree/BalanceTree/B-Tree/img/insert/8.png b/BinaryTree/BalanceTree/B-Tree/img/insert/8.png new file mode 100644 index 0000000..1bad29f Binary files /dev/null and b/BinaryTree/BalanceTree/B-Tree/img/insert/8.png differ diff --git a/BinaryTree/BalanceTree/B-Tree/img/insert/9.png b/BinaryTree/BalanceTree/B-Tree/img/insert/9.png new file mode 100644 index 0000000..7f3ae28 Binary files /dev/null and b/BinaryTree/BalanceTree/B-Tree/img/insert/9.png differ diff --git a/BinaryTree/BalanceTree/Red-Black Tree/README.MD b/BinaryTree/BalanceTree/Red-Black Tree/README.MD index a36e8e2..412dc7e 100644 --- a/BinaryTree/BalanceTree/Red-Black Tree/README.MD +++ b/BinaryTree/BalanceTree/Red-Black Tree/README.MD @@ -261,3 +261,54 @@ 你希望我画吗? +--- +没错 ✅,你总结得非常到位。 + +这是二叉搜索树(BST)**删除的基本套路**,红黑树只是额外多了颜色和修复步骤而已。 + +--- + +### 普通 BST 的删除分类 + +假设删除的节点是 `z`: + +1. **没有孩子(叶子)** + → 直接删。 + +2. **只有一个孩子** + → 用孩子替换它。 + +3. **有两个孩子** + → 找 **中序后继(右子树最小)** 或 **中序前驱(左子树最大)**, + + * 把 `y.key` 复制到 `z.key`; + * 实际删除 `y`(而 `y` 一定是“最多一个孩子”的情况)。 + +所以 **情况 3 总是能转化为情况 1 或 2**。 + +--- + +### 为什么后继/前驱最多只有一个孩子? + +比如用 **后继**(右子树最小的节点)来说明: + +* `y` 在 `z.right` 里,且 `y` 是最靠左的。 +* 所以 `y` 不可能有左孩子(否则就不是最左的了)。 +* 因此 `y` 要么没有孩子,要么只有右孩子。 + +对称地,用前驱的话,也必然是“没有孩子或只有左孩子”。 + +--- + +### 到红黑树这里 + +红黑树继承了 BST 结构,所以删除逻辑一样: + +* 如果 `z` 有两个孩子,**我们实际删的就是它的后继/前驱 `y`**,而不是直接删 `z`。 +* 这就是你说的“删除下移”。 + +真正复杂的地方在于:**如果被删的是黑色节点**,就需要一整套 `fixup` 来维持红黑树的平衡。 + +--- + +要不要我帮你把“3 种情况 → 转化为 2 种简单情况”的流程画成一个 **决策图(删除流程图)**?这样会更直观。 diff --git a/BinaryTree/BalanceTree/Red-Black Tree/info.jpg b/BinaryTree/BalanceTree/Red-Black Tree/info.jpg new file mode 100644 index 0000000..e5326b9 Binary files /dev/null and b/BinaryTree/BalanceTree/Red-Black Tree/info.jpg differ