数据结构11-二叉搜索数的删除

前面实现了二叉搜索树的添加以及二叉树的遍历,也知道了前驱节点后继节点的概念,现在就可以实现二叉搜索数的删除功能,实现一个完整的二叉搜索树了。

继续为二叉搜索树实现以下两个方法:

// 删除一个元素,即删除这个元素对应的节点
- (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;
        }
    }
}
复制代码

删除叶子节点的逻辑:

  1. 判断被删除节点如否是根节点,是进入第2步,否进入第3步。
  2. 直接将二叉树的根节点指向空
  3. 判断被删除节点是父节点的左子节点还是右子节点,左子节点进入第4步,右子节点进入第5步
  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的节点的逻辑:

  1. 判断被删除节点如否是根节点,是进入第2步,否进入第3步
  2. 被删除节点的度为1,只有一个子节点,获取被删除节点的子节点
  3. 将被删除节点的子节点的parent指向被删除节点的父节点
  4. 判断被删除节点是父节点的左子节点还是右子节点,左子节点进入第5步,右子节点进入第6步
  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

Click to view source code

Reproduced in: https: //juejin.im/post/5d076449e51d455a2f220266

Guess you like

Origin blog.csdn.net/weixin_34137799/article/details/93182191