• 【450. 删除二叉搜索树中的节点】


    来源:力扣(LeetCode)

    描述:

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

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

    首先找到需要删除的节点;
    如果找到了,删除它。

    示例 1:
    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]
    • 1
    • 2
    • 3
    • 4
    • 5

    1
    示例 2:

    输入: root = [5,3,6,2,4,null,7], key = 0
    输出: [5,3,6,2,4,null,7]
    解释: 二叉树不包含值为 0 的节点
    
    • 1
    • 2
    • 3

    示例 3:

    输入: root = [], key = 0
    输出: []
     
    
    • 1
    • 2
    • 3

    提示:

    • 节点数的范围 [0, 104].
    • -105<= Node.val <= 105
    • 节点值唯一
    • root 是合法的二叉搜索树
    • -105 <= key <= 105

    方法一:递归

    思路

    1
    2
    代码:

    class Solution {
    public:
        TreeNode* deleteNode(TreeNode* root, int key) {
            if (root == nullptr) {
                return nullptr;
            }
            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->val == key) {
                if (!root->left && !root->right) {
                    return nullptr;
                }
                if (!root->right) {
                    return root->left;
                }
                if (!root->left) {
                    return root->right;
                }
                TreeNode *successor = root->right;
                while (successor->left) {
                    successor = successor->left;
                }
                root->right = deleteNode(root->right, successor->val);
                successor->right = root->right;
                successor->left = root->left;
                return successor;
            }
            return root;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    执行用时:28 ms, 在所有 C++ 提交中击败了83.08%的用户
    内存消耗:31.8 MB, 在所有 C++ 提交中击败了71.37%的用户
    复杂度分析
    时间复杂度: O(n),其中 n 为 root 的节点个数。最差情况下,寻找和删除 successor 各需要遍历一次树。
    空间复杂度: O(n),其中 n 为 root 的节点个数。递归的深度最深为 O(n)。

    方法二:迭代

    思路

    方法一的递归深度最多为 n,而大部分是由寻找值为 key 的节点贡献的,而寻找节点这一部分可以用迭代来优化。寻找并删除 successor 时,也可以用一个变量保存它的父节点,从而可以节省一步递归操作。

    代码:

    class Solution {
    public:
        TreeNode* deleteNode(TreeNode* root, int key) {
            TreeNode *cur = root, *curParent = nullptr;
            while (cur && cur->val != key) {
                curParent = cur;
                if (cur->val > key) {
                    cur = cur->left;
                } else {
                    cur = cur->right;
                }
            }
            if (!cur) {
                return root;
            }
            if (!cur->left && !cur->right) {
                cur = nullptr;
            } else if (!cur->right) {
                cur = cur->left;
            } else if (!cur->left) {
                cur = cur->right;
            } else {
                TreeNode *successor = cur->right, *successorParent = cur;
                while (successor->left) {
                    successorParent = successor;
                    successor = successor->left;
                }
                if (successorParent->val == cur->val) {
                    successorParent->right = successor->right;
                } else {
                    successorParent->left = successor->right;
                }
                successor->right = cur->right;
                successor->left = cur->left;
                cur = successor;
            }
            if (!curParent) {
                return cur;
            } else {
                if (curParent->left && curParent->left->val == key) {
                    curParent->left = cur;
                } else {
                    curParent->right = cur;
                }
                return root;
            }
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    执行用时:28 ms, 在所有 C++ 提交中击败了83.08%的用户
    内存消耗:31.8 MB, 在所有 C++ 提交中击败了86.56%的用户
    复杂度分析
    时间复杂度: O(n),其中 n 为 root 的节点个数。最差情况下,需要遍历一次树。
    空间复杂度: O(1)。使用的空间为常数。
    author:LeetCode-Solution

  • 相关阅读:
    单例模式(饿汉式单例 VS 懒汉式单例)
    记录学习--lombok @Generated
    浩源鼎盛科技:你真的了解抖音推送机制吗?
    十三、MySql的视图
    JVM 内存调优总结贴
    模拟IIC通讯协议(stm32)(硬件iic后面在补)
    eureka
    Vue_路由VueRoute
    母婴用品会员商城小程序的作用是什么
    【Spring】注解
  • 原文地址:https://blog.csdn.net/Sugar_wolf/article/details/125535508