前面实现了二叉搜索树的添加以及二叉树的遍历,也知道了前驱节点和后继节点的概念,现在就可以实现二叉搜索数的删除功能,实现一个完整的二叉搜索树了。
继续为二叉搜索树实现以下两个方法:
// 删除一个元素,即删除这个元素对应的节点
- (void)removeObject:(id)object {
[self removeWithNode:[self nodeWithObject:object]];
}
// 删除一个节点
- (void)removeWithNode:(JKRBinaryTreeNode *)node {
}
复制代码
删除叶子节点
如上图,删除二叉树中的叶子节点,只需要移除该节点就可以,二叉树依然满足二叉搜索树的性质。
// 叶子节点既没有左子树也没有右子树
if (!node.left && !node.right) {
// 如果是根节点就是直接让根节点为空
if (!node.parent) {
_root = nil;
} else {
// 如果是父节点的左子节点,就让父节点的左字节点为空
if (node == node.parent.left) {
node.parent.left = nil;
} else {
node.parent.right = nil;
}
}
}
复制代码
删除叶子节点的逻辑:
- 判断被删除节点如否是根节点,是进入第2步,否进入第3步。
- 直接将二叉树的根节点指向空
- 判断被删除节点是父节点的左子节点还是右子节点,左子节点进入第4步,右子节点进入第5步
- 将断被删除节点的父节点的左子节点置空
- 将断被删除节点的父节点的右子节点置空
根据如上逻辑,删除二叉树中的 5 和 8:
[tree removeObject:@5];
┌-7 (p: (null))-┐
│ │
┌-4 (p: 7) ┌-9 (p: 7)-┐
│ │ │
┌-2 (p: 4)-┐ 8 (p: 9) ┌-11 (p: 9)-┐
│ │ │ │
1 (p: 2) 3 (p: 2) 10 (p: 11) 12 (p: 11)
[tree removeObject:@8];
┌-7 (p: (null))-┐
│ │
┌-4 (p: 7) 9 (p: 7)-┐
│ │
┌-2 (p: 4)-┐ ┌-11 (p: 9)-┐
│ │ │ │
1 (p: 2) 3 (p: 2) 10 (p: 11) 12 (p: 11)
复制代码
删除度为1的节点
如上图,删除度为1节点后,非常类似于删除链表的节点的操作,只需要将节点的父节点指向它的指针指向它自己的字节点就可以:
if ((node.left && !node.right) || (node.right && !node.left)) {
JKRBinaryTreeNode *replacement = node.left ? node.left : node.right;
// 一定不要忘记度parent的维护
replacement.parent = node.parent;
if (!node.parent) {
_root = replacement;
} else if(node == node.parent.left) {
node.parent.left = replacement;
} else {
node.parent.right = replacement;
}
}
复制代码
删除度为1的节点的逻辑:
- 判断被删除节点如否是根节点,是进入第2步,否进入第3步
- 被删除节点的度为1,只有一个子节点,获取被删除节点的子节点
- 将被删除节点的子节点的parent指向被删除节点的父节点
- 判断被删除节点是父节点的左子节点还是右子节点,左子节点进入第5步,右子节点进入第6步
- 将断被删除节点的父节点的左子节点指向被删除节点的子节点
- 将断被删除节点的父节点的右子节点指向被删除节点的子节点
根据如上逻辑,删除二叉树中的 4 和 9:
[tree removeObject:@4];
┌-7 (p: (null))-┐
│ │
┌-2 (p: 7)-┐ 9 (p: 7)-┐
│ │ │
1 (p: 2) 3 (p: 2) ┌-11 (p: 9)-┐
│ │
10 (p: 11) 12 (p: 11)
[tree removeObject:@9];
┌----7 (p: (null))----┐
│ │
┌-2 (p: 7)-┐ ┌-11 (p: 7)-┐
│ │ │ │
1 (p: 2) 3 (p: 2) 10 (p: 11) 12 (p: 11)
复制代码
一定不要忘记对parent的维护。
删除度为2的节点
二叉搜索树的中度为2的节点如下图:
可以看到这些节点都有两个子节点,即同时存在左子树和右子树,这些节点不能直接移除,也不能直接让它的一个子节点替代它的位置,但是可以根据如下方法删除:
- 找到被删除节点的后继节点(或前驱节点)做为替代节点
- 将替代节点的保存的元素移动到被删除节点中
- 删除替代节点
For example, delete the above binary search tree root 7:
First find the successor nodes of node 7 as an alternative
The value of the successor nodes to the root node is moved
Delete successor nodes:
We can see, after this operation, binary or a binary search tree.
Here successor node to be analyzed, the precursor node empathy. A successor node is the node in order traversal, a node, the sequence of binary search tree traversal is good in ascending order. Therefore, the binary search tree, a node of the successor node element immediately after ascending order according to the one element.
A binary search tree node of degree 2, and its successor node in certain of its right subtree, and the degree is not necessarily the node 2, it can only be 1 or 0 degree.
the reason:
- Binary search tree traversal sequence happens to be in ascending order, so that its successor nodes must be in accordance with the ascending node
- Binary search tree left subtree of any node all the elements than it is small, all the elements in the right subtree than it big
- Therefore, a successor node of node 2, it must be in the right subtree, and its right subtree is the smallest of all the elements
- So a degree successor node 2, will not exist in the left subtree. Because if it has left sub-tree, it is not minimal.
Fully logically find a binary search tree node or a successor node of the predecessor node follows:
#pragma mark - 节点的前驱节点
- (JKRBinaryTreeNode *)predecessorWithNode:(JKRBinaryTreeNode *)node {
if (!node) {
return nil;
}
// 节点有左子树的情况下,前驱节点在它的左子树中
if (node.left) {
// 前驱节点是 node.left.right.right...
JKRBinaryTreeNode *p = node.left;
while (p.right) {
p = p.right;
}
return p;
}
// 节点没有左子树的情况下,如果有父节点,在父节点往上找
while (node.parent && node == node.parent.left) {
node = node.parent;
}
// 没有左子树,也没有父节点,就没有前驱节点
// !node.left && (!node.parent || node == node.parent.right)
return node.parent;
}
#pragma mark - 节点的后继节点
- (JKRBinaryTreeNode *)successorWithNode:(JKRBinaryTreeNode *)node {
if (!node) {
return nil;
}
if (node.right) {
JKRBinaryTreeNode *p = node.right;
while (p.left) {
p = p.left;
}
return p;
}
while (node.parent && node == node.parent.right) {
node = node.parent;
}
return node.parent;
}
复制代码
Deleted degree 2 nodes, the successor node simply find the first node and the successor node value after the movement, and directly above the delete node of a logic 0 or 1 can be:
if (node.left && node.right) {
JKRBinaryTreeNode *s = [self successorWithNode:node];
node.object = s.object;
node = s;
}
if ((node.left && !node.right) || (node.right && !node.left)) {
JKRBinaryTreeNode *replacement = node.left ? node.left : node.right;
// 一定不要忘记度parent的维护
replacement.parent = node.parent;
if (!node.parent) {
_root = replacement;
} else if(node == node.parent.left) {
node.parent.left = replacement;
} else {
node.parent.right = replacement;
}
}
if (!node.left && !node.right) {
// 如果是根节点就是直接让根节点为空
if (!node.parent) {
_root = nil;
} else {
// 如果是父节点的左子节点,就让父节点的左字节点为空
if (node == node.parent.left) {
node.parent.left = nil;
} else {
node.parent.right = nil;
}
}
}
复制代码
The following binary search tree delete operations in sequence:
┌---7 (p: (null))---┐
│ │
┌-4 (p: 7)-┐ ┌-9 (p: 7)-┐
│ │ │ │
┌-2 (p: 4)-┐ 5 (p: 4) 8 (p: 9) ┌-11 (p: 9)-┐
│ │ │ │
1 (p: 2) 3 (p: 2) 10 (p: 11) 12 (p: 11)
[tree removeObject:@7];
┌-8 (p: (null))-┐
│ │
┌-4 (p: 8)-┐ 9 (p: 8)-┐
│ │ │
┌-2 (p: 4)-┐ 5 (p: 4) ┌-11 (p: 9)-┐
│ │ │ │
1 (p: 2) 3 (p: 2) 10 (p: 11) 12 (p: 11)
[tree removeObject:@4];
┌-8 (p: (null))-┐
│ │
┌-5 (p: 8) 9 (p: 8)-┐
│ │
┌-2 (p: 5)-┐ ┌-11 (p: 9)-┐
│ │ │ │
1 (p: 2) 3 (p: 2) 10 (p: 11) 12 (p: 11)
[tree removeObject:@8];
┌-9 (p: (null))-┐
│ │
┌-5 (p: 9) ┌-11 (p: 9)-┐
│ │ │
┌-2 (p: 5)-┐ 10 (p: 11) 12 (p: 11)
│ │
1 (p: 2) 3 (p: 2)
复制代码
Delete Code Summary
The above three cases combined code:
- (void)removeWithNode:(JKRBinaryTreeNode *)node {
if (!node) {
return;
}
_size--;
if (node.hasTwoChildren) {
JKRBinaryTreeNode *s = [self successorWithNode:node];
node.object = s.object;
node = s;
}
// 实际被删除节点的子节点
JKRBinaryTreeNode *replacement = node.left ? node.left : node.right;
if (replacement) { // 被删除的节点度为1
replacement.parent = node.parent;
if (!node.parent) {
_root = replacement;
} else if (node == node.parent.left) {
node.parent.left = replacement;
} else {
node.parent.right = replacement;
}
} else {
if(!node.parent) { // 被删除的节点度为0且没有父节点,被删除的节点是根节点且二叉树只有一个节点
_root = nil;
} else { // 被删除的节点是叶子节点且不是根节点
if (node.isLeftChild) {
node.parent.left = nil;
} else {
node.parent.right = nil;
}
}
}
}
复制代码
Source
Reproduced in: https: //juejin.im/post/5d076449e51d455a2f220266