文章目录
- 题目
- 标题和出处
- 难度
- 题目描述
- 要求
- 示例
- 数据范围
- 进阶
- 解法一
- 思路和算法
- 代码
- 复杂度分析
- 解法二
- 思路和算法
- 代码
- 复杂度分析
题目
标题和出处
标题:删除二叉搜索树中的结点
出处:450. 删除二叉搜索树中的结点
难度
5 级
题目描述
要求
给定二叉搜索树的根结点 root \texttt{root} root 和一个值 key \texttt{key} key,删除二叉搜索树中的值为 key \texttt{key} key 的结点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根结点的引用。
一般而言,删除结点可分为两个步骤:
- 首先找到需要删除的结点。
- 如果找到了需要删除的结点,删除该结点。
示例
示例 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]。
示例 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} -105≤Node.val≤105
- 每个结点值各不相同
- root \texttt{root} root 是二叉搜索树
- -10 5 ≤ key ≤ 10 5 \texttt{-10}^\texttt{5} \le \texttt{key} \le \texttt{10}^\texttt{5} -105≤key≤105
进阶
树的高度为 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)。