1. 如果键 在节点 中,且 是叶子节点,则从 中删除键 。
2. 如果键 在节点 中,且 是内部节点,请执行以下操作:
a. 如果节点 中位于 之前的子节点 至少有 个键,则在以 为根的子树中找到 的前驱值 。将 中的 替换为 ,并递归地删除 。
b. 对称地,如果节点 中位于 后面的子节点 至少有 个键,则在以 为根的子树中找到 的后继值 。将 中的 替换为 ,并递归地删除 。
c. 否则,如果 和 都只有 键,那么将 和 都合并到 中,然后,递归地从 中删除 。
3. 如果键 不存在于内部节点 中,则确定必须包含 的相应子树的根 。如果 只有 键,则根据需要执行步骤 3a 或 3b,以确保下降到至少包含 键的节点。
a. 如果 只有 个键,但有一个具有 个键的兄弟节点,则通过将一个键从 向下移动到 中,将一个键从 的左或右同级的兄弟节点中向上移动到 中,并将相应的子级从兄弟节点中移动到 中,为 提供一个额外的键。(《算导》上没说明)
b. 如果 和所有 的兄弟节点都只有 键,则将 与其中一个同级节点合并,并将一个键从 向下移动到新的合并节点中,以成为该节点的中间键。
我一开始看得时候,没有分清楚算法中强调“x 是内部节点,但是 k 在其中或者不在其中”这个条件的意义。后来发现,这样说,只是为了方便在节点下降的过程中(搜索 k 的过程中),就已经开始进行 merge 或者 sibling 操作,而不需要等到回溯的过程中进行 merge 和 sibling。
也有回溯版本的,从最底层往上进行 merge 和 sibling 操作:https://www.cnblogs.com/nullzx/p/8729425.html