二叉搜索树操作题目:删除二叉搜索树中的结点

本文介绍了如何在二叉搜索树中删除指定值的节点,包括递归和迭代两种方法。通过比较节点值进行查找,并处理删除叶节点、单侧子节点以及有两侧子节点的情况,确保树的性质保持。两种方法的时间复杂度均为O(n),但迭代方法的空间复杂度较低为O(1)。

题目

标题和出处

标题:删除二叉搜索树中的结点

出处:450. 删除二叉搜索树中的结点

难度

5 级

题目描述

要求

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

一般而言,删除结点可分为两个步骤:

  1. 首先找到需要删除的结点。
  2. 如果找到了需要删除的结点,删除该结点。

示例

示例 1:

示例 1.1

输入: root   =   [5,3,6,2,4,null,7],   key   =   3 \texttt{root = [5,3,6,2,4,null,7], key = 3} root = [5,3,6,2,4,null,7], key = 3
输出: [5,4,6,2,null,null,7] \texttt{[5,4,6,2,null,null,7]} [5,4,6,2,null,null,7]
解释:给定需要删除的结点值是 3 \texttt{3} 3,所以我们首先找到值为 3 \texttt{3} 3 的结点,然后删除它。
一个正确的答案是 [5,4,6,2,null,null,7] \texttt{[5,4,6,2,null,null,7]} [5,4,6,2,null,null,7],如上图所示。
另一个正确的答案是 [5,2,6,null,4,null,7] \texttt{[5,2,6,null,4,null,7]} [5,2,6,null,4,null,7]

示例 1.2

示例 2:

输入: root   =   [5,3,6,2,4,null,7],   key   =   0 \texttt{root = [5,3,6,2,4,null,7], key = 0} root = [5,3,6,2,4,null,7], key = 0
输出: [5,3,6,2,4,null,7] \texttt{[5,3,6,2,4,null,7]} [5,3,6,2,4,null,7]
解释:二叉搜索树不包含值为 0 \texttt{0} 0 的结点。

示例 3:

输入: root   =   [],   key   =   0 \texttt{root = [], key = 0} root = [], key = 0
输出: [] \texttt{[]} []

数据范围

  • 树中结点数目在范围 [0,   10 4 ] \texttt{[0, 10}^\texttt{4}\texttt{]} [0, 104]
  • -10 5 ≤ Node.val ≤ 10 5 \texttt{-10}^\texttt{5} \le \texttt{Node.val} \le \texttt{10}^\texttt{5} -105Node.val105
  • 每个结点值各不相同
  • root \texttt{root} root 是二叉搜索树
  • -10 5 ≤ key ≤ 10 5 \texttt{-10}^\texttt{5} \le \texttt{key} \le \texttt{10}^\texttt{5} -105key105

进阶

树的高度为 h h h,你是否可以想出时间复杂度为 O ( h ) O(h) O(h) 的解法?

解法一

思路和算法

为了删除二叉搜索树中的结点,需要首先搜索待删除的结点,只有当待删除的结点存在时才需要执行删除结点的操作。

由于在二叉搜索树中搜索特定值的结点是一个递归的过程,因此可以使用递归实现删除结点的操作。递归的终止条件是当前结点为空,此时不需要删除任何结点,返回空二叉树。对于其余情况,比较根结点值和待删除的结点值,决定应该在根结点的哪个子树中删除结点,对该子树调用递归,并用递归调用的结果更新当前结点的相应子树。

  • 如果根结点值大于目标值,则应该在根结点的左子树中删除结点。

  • 如果根结点值小于目标值,则应该在根结点的右子树中删除结点。

  • 如果根结点值等于目标值,则需要删除根结点。判断根结点的左子结点和右子结点是否为空,分别执行相应的操作。

    • 如果根结点的两个子结点都为空,即根结点是叶结点,则直接删除根结点,返回空二叉树。

    • 如果根结点有一个子结点为空,则删除根结点之后剩下非空子树,返回根结点的非空子树。

    • 如果根结点的两个子结点都不为空,则找到根结点的后继结点,后继结点为根结点的右子树中的最右边的结点,将根结点值设为后继结点值,将后继结点设为其右子结点(需要通过对后继结点的父结点更新相应的子结点实现)。

代码

class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        if (root == null) {
            return null;
        }
        if (root.val > key) {
            root.left = deleteNode(root.left, key);
            return root;
        }
        if (root.val < key) {
            root.right = deleteNode(root.right, key);
            return root;
        }
        if (root.left == null && root.right == null) {
            return null;
        } else if (root.left == null) {
            return root.right;
        } else if (root.right == null) {
            return root.left;
        } else {
            TreeNode node = root.right, parent = root;
            while (node.left != null) {
                parent = node;
                node = node.left;
            }
            root.val = node.val;
            if (parent == root) {
                parent.right = node.right;
            } else {
                parent.left = node.right;
            }
            return root;
        }
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉搜索树的结点数。时间复杂度取决于二叉搜索树的高度,最坏情况下二叉搜索树的高度是 O ( n ) O(n) O(n)

  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉搜索树的结点数。空间复杂度主要是递归调用的栈空间,取决于二叉搜索树的高度,最坏情况下二叉搜索树的高度是 O ( n ) O(n) O(n)

解法二

思路和算法

递归实现可以改成迭代实现。首先在二叉搜索树中搜索待删除的结点,同时维护待删除的结点的父结点。如果待删除的结点不存在,则不执行删除操作,返回原二叉搜索树。

如果待删除的结点存在,则判断待删除的结点的父结点是否为空结点。如果待删除的结点的父结点为空,则待删除的结点为二叉搜索树的根结点,在整个二叉搜索树中执行删除操作;如果待删除的结点的父结点不为空,则在以待删除的结点为根结点的子树中执行删除操作,然后用执行删除操作之后的结果更新父结点的相应子树。

当原始二叉搜索树或者其中一个子树的根结点为待删除的结点时,删除操作如下。

  • 如果根结点的两个子结点都为空,即根结点是叶结点,则直接删除根结点,返回空二叉树。

  • 如果根结点有一个子结点为空,则删除根结点之后剩下非空子树,返回根结点的非空子树。

  • 如果根结点的两个子结点都不为空,则找到根结点的后继结点,后继结点为根结点的右子树中的最右边的结点,将根结点值设为后继结点值,将后继结点设为其右子结点(需要通过对后继结点的父结点更新相应的子结点实现)。

代码

class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        TreeNode node = root, parent = null;
        while (node != null && node.val != key) {
            parent = node;
            if (node.val > key) {
                node = node.left;
            } else {
                node = node.right;
            }
        }
        if (node == null) {
            return root;
        }
        if (parent == null) {
            return deleteRoot(root);
        } else {
            if (node == parent.left) {
                parent.left = deleteRoot(node);
            } else {
                parent.right = deleteRoot(node);
            }
            return root;
        }
    }

    public TreeNode deleteRoot(TreeNode root) {
        if (root.left == null && root.right == null) {
            return null;
        } else if (root.left == null) {
            return root.right;
        } else if (root.right == null) {
            return root.left;
        } else {
            TreeNode parent = root, node = root.right;
            while (node.left != null) {
                parent = node;
                node = node.left;
            }
            root.val = node.val;
            if (node == parent.right) {
                parent.right = node.right;
            } else {
                parent.left = node.right;
            }
            return root;
        }
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉搜索树的结点数。时间复杂度取决于二叉搜索树的高度,最坏情况下二叉搜索树的高度是 O ( n ) O(n) O(n)

  • 空间复杂度: O ( 1 ) O(1) O(1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟大的车尔尼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值