“Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。”
一、题目描述
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
- 首先找到需要删除的节点;
- 如果找到了,删除它。
示例 1:
输入:root = [5,3,6,2,4,null,7], key = 3
输出:[5,4,6,2,null,null,7]
解释:给定需要删除的节点值是 3,所以我们首先找到 3 这个节点,然后删除它。
一个正确的答案是 [5,4,6,2,null,null,7], 另一个正确答案是 [5,2,6,null,4,null,7]。
示例 2:
输入: root = [5,3,6,2,4,null,7], key = 0
输出: [5,3,6,2,4,null,7]
解释: 二叉树不包含值为 0 的节点
示例 3:
输入: root = [], key = 0
输出: []
提示:
- 节点数的范围
[0, 104] -105 <= Node.val <= 105- 节点值唯一
root是合法的二叉搜索树-105 <= key <= 105
进阶: 要求算法时间复杂度为 O(h),h 为树的高度。
二、思路分析
抓住“二叉搜索树” 左小右大 的特点,我们判断遍历到的节点的值与 key 的大小关系,根据这个关系选择遍历路径。然后在这条路径上找到目标节点后,就可以准备删除该节点了。
需要注意,要删除的节点有三种情况:
- ①该节点恰好是末端节点,不存在左节点和有节点,返回
nullptr即可。 - ②该节点只有一个子节点,若没有左节点则将右节点作为取代节点,若没有右节点则将左节点作为取代节点。
- ③该节点有两个子节点,我们要保证在删除了该节点后,这棵树依然是一颗二叉搜索树,因此需要找到新的节点来替代被删除的节点。要达到这个要求,有两种策略:
- 遍历该节点的左子树的右子树,遍历到的末端节点就为取代节点。
二叉搜索树的根节点的左节点肯定小于右节点,因此不管要删除的节点的左子树遍历到什么程度,节点值都肯定小于要删除节点的右节点值;但如果遍历到末尾,这个末端节点值肯定是大于要删除节点的左节点。
- 遍历该节点的右子树的左子树,遍历到的末端节点就为取代节点。
右子树的遍历也是同样的道理。
清楚了这三种情况后,我们可以来删除节点了,思路如下:
- 找到要删除的节点
root,寻找root的右子树的最小节点cur(选择第二种策略)。 - 将
root的左子树放在cur左子树的位置(砍掉root的左子树,就剩下右子树没有砍了)。 - 由于我们是递归查看节点的,那么右子树的处理就看最后要
return什么了。这里先给结论return root -> right,为什么是这样呢?不应该是return cur吗?其实不然,因为如果cur接上root左子树之后,加上它本身的右子树(如果有的话)和它的根节点,就已经有三个连接了,如果将cur作为取代节点的话,就不满足二叉树的定义了,因此不能return cur。那么这样一来,最好的选择就是root -> right,将其作为取代节点,因为它本身就满足 左大右小 的特点,并且在root被删除掉之后,本来与root的连接就断开了,此时它最多有两个连接而已,作为取代节点再合适不过了。
最难的部分已经攻克了,答案也不难写了。
三、AC 代码
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if (!root) return nullptr;
if (root -> val > key) root-> left = deleteNode(root -> left, key);
if (root -> val < key) root -> right = deleteNode(root -> right, key);
if (root -> val == key) {
if (!root -> left && !root -> right) return nullptr;
if (!root -> left) return root -> right;
if (!root -> right) return root -> left;
// 找到右子树最小的节点
TreeNode* cur = root -> right;
while (cur -> left) {
cur = cur -> left;
}
cur -> left = root -> left;
return root -> right;
}
return root;
}
};