代码随想录算法训练营第二十二天|235. 二叉搜索树的最近公共祖先、701. 二叉搜索树中的插入操作、450. 删除二叉搜索树中的节点

LeetCode 235. 二叉搜索树的最近公共祖先

链接:235. 二叉搜索树的最近公共祖先

思路:

这道题目相比于上一道题236. 二叉树的最近公共祖先要简单不少,因为可以利用二叉搜索树的性质。虽然可以用和二叉树同样的解法,但是合理利用二叉搜索树的性质的话可以让代码简单很多。其思路就是,对于一个二叉搜素数的两个节点p、q,它们的最近公共祖先root一定符合一下性质:

root->val <= max(p->val, q->val) && root->val >= min(p->val, q->val)

也就是说它们的最近公共祖先一定是夹在p和q两者之间的。根据236. 二叉树的最近公共祖先我们可以知道,两个节点的最近公共祖先一定要满足两个节点不在同一个子树上面,也就是说不能出现p、q同时大于root或者小于root的情况(但其中一个可以等于root),这么一来只需要递归查找到符合条件的root节点即可。

代码:

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        int lower = p->val < q->val?p->val:q->val;
        int upper = p->val > q->val?p->val:q->val;
        return dfs(root, lower, upper);
    }
    TreeNode* dfs(TreeNode* root, int lower, int upper)
    {
        if (root->val > upper)
            return dfs(root->left, lower, upper);
        else if (root->val < lower)
            return dfs(root->right, lower, upper);
        else
            return root;
    }
};

LeetCode 701. 二叉搜索树中的插入操作

链接:701. 二叉搜索树中的插入操作

思路:

其实是非常简单的一道题,不知道为什么是中等难度。最重要的一点就是要找到插入的位置,根据左节点小于中间节点,右节点大于中间节点的规则,可以递归写出寻找插入位置的逻辑:当插入的值小于当前节点,往左子树查找,大于当前节点,往右子树查找,直到找到一个叶子节点后,把节点插入到叶子节点的子节点即可。
代码:

class Solution {
public:
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        if (root == nullptr)
        {
            root = new TreeNode(val);
            return root;
        }
            
        if (root->val > val)
            root->left = insertIntoBST(root->left, val);
        else
            root->right = insertIntoBST(root->right, val);
        return root;
    }
};

LeetCode 450. 删除二叉搜索树中的节点

链接:450. 删除二叉搜索树中的节点

思路:

这题可比二叉搜索树节点插入操作难多了,要考虑很多种情况。首先是递归查找的逻辑,当没有找到要删除的节点,直接返回null,如果当前节点值大于key,递归查找左子树的节点,反之则查找右子树的节点,最后也是最重要的部分则是处理找到要删除的节点的情况。

要删除的节点也分为很多种情况。

1. 节点的左右孩子都为空,直接delete当前节点然后返回空即可。

            // 左右孩子都为空节点,删除当前节点,直接返回空节点
            if (root->left == nullptr && root->right == nullptr)
            {
                delete(root);
                return nullptr;
            }

2&3. 左孩子不为空,右孩子为空,或者右孩子为空,左孩子不为空,直接返回不为空的孩子节点即可。

            // 左孩子为空,右孩子不为空,直接返回右孩子节点
            else if (root->left == nullptr && root->right != nullptr)
            {
                TreeNode* right = root->right;
                delete(root);
                return right;
            }
            // 右孩子为空,左孩子不为空,直接返回左孩子节点
            else if (root->left != nullptr && root->right == nullptr)
            {
                TreeNode* left = root->left;
                delete(root);
                return left;
            }

4. 左右孩子都不为空,这种情况最为复杂,操作方法为:把左孩子的整颗子树放右孩子最左边的左节点上。为什么这么操作呢?仔细想想中序遍历的顺序,是先遍历到左孩子整颗子树后,再遍历中间节点,再从右子树的最左边节点开始的,但是这个时候中间节点被删除了,也就是说左子树的根节点马上接的就是右子树的最左边节点,所以才要把左边子树放在右孩子最左边的左节点上。函数changeNode()就是完成这个操作的。

            // 左右孩子都不为空的情况
            // 把左孩子放右孩子的最左边,返回右孩子的头节点
            else
            {
                TreeNode* left = root->left;
                changeNode(root->right, left);
                TreeNode* right = root->right;
                delete(root);
                return right;
            }

完整代码:

class Solution {
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        if (root == nullptr)
            return nullptr;
        if (root->val > key)
            root->left = deleteNode(root->left, key);
        else if (root->val < key)
            root->right = deleteNode(root->right, key);
        else // root节点即为需要删除的节点
        {
            // 左右孩子都为空节点,删除当前节点,直接返回空节点
            if (root->left == nullptr && root->right == nullptr)
            {
                delete(root);
                return nullptr;
            }
            // 左孩子为空,右孩子不为空,直接返回右孩子节点
            else if (root->left == nullptr && root->right != nullptr)
            {
                TreeNode* right = root->right;
                delete(root);
                return right;
            }
            // 右孩子为空,左孩子不为空,直接返回左孩子节点
            else if (root->left != nullptr && root->right == nullptr)
            {
                TreeNode* left = root->left;
                delete(root);
                return left;
            }
            // 左右孩子都不为空的情况
            // 把左孩子放右孩子的最左边,返回右孩子的头节点
            else
            {
                TreeNode* left = root->left;
                changeNode(root->right, left);
                TreeNode* right = root->right;
                delete(root);
                return right;
            }
        }
        return root;
    }
    // 用于把left节点放在root节点最左边孩子的位置
    void changeNode(TreeNode* root, TreeNode* left)
    {
        while (root->left)
            root = root->left;
        root->left = left;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值