LeetCode 450: 删除二叉搜索树中的节点 Delete Node in a BST

题目:

给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。

Given a root node reference of a BST and a key, delete the node with the given key in the BST. Return the root node reference (possibly updated) of the BST.

一般来说,删除节点可分为两个步骤:

  1. 首先找到需要删除的节点;

  2. 如果找到了,删除它。

Basically, the deletion can be divided into two stages:

  1. Search for a node to remove.

  2. If the node is found, delete the node.

说明: 要求算法时间复杂度为 O(h),h 为树的高度。

Note: Time complexity should be O(height of tree).

示例:

root = [5,3,6,2,4,null,7]
key = 3

    5
   / \
  3   6
 / \   \
2   4   7

给定需要删除的节点值是 3,所以我们首先找到 3 这个节点,然后删除它。

一个正确的答案是 [5,4,6,2,null,null,7], 如下图所示。

    5
   / \
  4   6
 /     \
2       7

另一个正确答案是 [5,2,6,null,4,null,7]。

    5
   / \
  2   6
   \   \
    4   7

解题思路:

待删除节点在二叉树中的三种情况有:

  1. 如果目标节点没有子节点,我们可以直接移除该目标节点。

  2. 如果目标节只有一个子节点,我们可以用其子节点作为替换。

  3. 如果目标节点有两个子节点,我们需要用其中序后继节点或者前驱节点来替换,再删除该目标节点。

另外二叉搜索树的中序遍历结果为从小到大顺序排列的; 删除节点如果不是叶子节点时, 则应把该节点的值替换为其右子树中最小的一个节点值 (删除节点的后驱节点); 删除节点如果不是叶子节点且无右子树时, 则应把该节点的值替换为其左子树中最大的一个节点值 (删除节点的前驱节点), 并在子树中递归删除刚刚替换的节点

你会发现, 二叉搜索树最小节点为该树的最左叶子; 最大节点为该树的最右叶子, 即:

  • 如果 key > root.val,说明要删除的节点在右子树,root.right = deleteNode(root.right, key)。

  • 如果 key < root.val,说明要删除的节点在左子树,root.left = deleteNode(root.left, key)。

  • 如果 key == root.val,则该节点就是我们要删除的节点,则:

    • 如果该节点是叶子节点,则直接删除它:root = null。

    • 如果该节点不是叶子节点且有右节点,则用它的后继节点的值替代 root.val = successor.val,然后删除后继节点。

    • 如果该节点不是叶子节点且只有左节点,则用它的前驱节点的值替代 root.val = predecessor.val,然后删除前驱节点。

Java:

class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        if (root == null)
            return null;
        // 找到待删除节点后的三种情况
        if (key == root.val) {
            // 删除节点为叶子节点
            if (root.left == null && root.right == null)
                root = null;
            // 删除节点有右子树
            else if (root.right != null) {
                root.val = postNode(root.right);
                root.right = deleteNode(root.right, root.val);
            } else {// 删除节点无右子树但存在左子树
                root.val = preNode(root.left);
                root.left = deleteNode(root.left, root.val);
            }
        } else if (key > root.val)
            root.right = deleteNode(root.right, key);
        else
            root.left = deleteNode(root.left, key);
        return root;
    }
    // 找左子树的最大节点
    private int preNode(TreeNode root) {
        while (root.right != null)
            root = root.right;
        return root.val;
    }
    // 找右子树的最小节点
    private int postNode(TreeNode root) {
        while (root.left != null)
            root = root.left;
        return root.val;
    }
}

Python:

class Solution:
    def deleteNode(self, root: TreeNode, key: int) -> TreeNode:
        if not root:
            return None
        # 找到待删除节点后的三种情况
        if root.val == key:
            # 删除节点为叶子节点
            if not root.left and not root.right:
                root = None
            # 删除节点有右子树
            elif root.right:
                root.val = self.postNode(root.right)
                root.right = self.deleteNode(root.right, root.val)
            # 删除节点无右子树但存在左子树
            else:
                root.val = self.preNode(root.left)
                root.left = self.deleteNode(root.left, root.val)
        elif key > root.val:
            root.right = self.deleteNode(root.right, key)
        else:
            root.left = self.deleteNode(root.left, key)
        return root
    # 找左子树的最大节点
    def preNode(self, root: TreeNode) -> int:
        while root.right:
            root = root.right
        return root.val
    # 找右子树的最小节点
    def postNode(self, root: TreeNode) -> int:
        while root.left:
            root = root.left
        return root.val

Golang:

func deleteNode(root *TreeNode, key int) *TreeNode {
    if root == nil {
        return nil
    }
    // 找到待删除节点后的三种情况
    if root.Val == key {
        // 删除节点为叶子节点
        if root.Left == nil && root.Right == nil {
            root = nil
        } else if root.Right != nil {// 删除节点有右子树
            root.Val = postNode(root.Right)
            root.Right = deleteNode(root.Right, root.Val)
        } else {// 删除节点无右子树但存在左子树
            root.Val = preNode(root.Left)
            root.Left = deleteNode(root.Left, root.Val)
        }
    } else if key > root.Val {
        root.Right = deleteNode(root.Right, key)
    } else {
        root.Left = deleteNode(root.Left, key)
    }
    return root
}
// 找左子树的最大节点
func preNode(root *TreeNode) int {
    for root.Right != nil {
        root = root.Right
    }
    return root.Val
}
// 找右子树的最小节点
func postNode(root *TreeNode) int {
    for root.Left != nil {
        root = root.Left
    }
    return root.Val
}

667e96db711ff2fa074ad12ab5fdcadf.jpeg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值