Bootstrap

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

代码随想录算法训练营

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



前言

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

讲解文档

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

讲解文档

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

讲解文档


一、LeetCode235. 二叉搜索树的最近公共祖先

1.题目链接

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

2.思路

(1)二叉搜索树最近公共祖先的判定:
1)由于二叉搜索树中,左子树节点<中间节点<右子树节点,所以如果第一次遇到cur->val在p->val和q->val之间,那么p,q分别在cur的左右子树中,cur就是公共祖先
2)为什么cur一定是最近公共祖先:
p,q分别在cur的左右子树中,假设p在左子树,q在右子树,如果cur向左遍历则不能作为q的祖先,向右遍历则不能作为p的祖先,所以cur是最近公共祖先
(2)递归
1)终止:空节点返回
2)单层递归:
① 如果cur->val 比p->val和q->val都大,那么最近公共祖先比cur->val小,向左遍历
也就是到cur的左子树中寻找最近公共祖先,只要最近公共祖先,不要空的节点
② 如果cur->val 比p->val和q->val都小,那么最近公共祖先比cur->val大,向右遍历
也就是到cur的右子树中寻找最近公共祖先,只要最近公共祖先,不要空的节点
③ 剩下的是cur->val在p->val和q->val之间,cur一定是最近公共祖先

3.题解

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (root == NULL)
            return root;

        if (root->val > p->val && root->val > q->val) {
            TreeNode* left = lowestCommonAncestor(root->left, p, q);
            if (left != NULL)
                return left;
        } else if (root->val < p->val && root->val < q->val) {
            TreeNode* right = lowestCommonAncestor(root->right, p, q);
            if (right != NULL)
                return right;
        }
        return root;
    }
};

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

1.题目链接

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

2.思路

(1)由于题目要求返回任何合理的二叉树均可,所以我在这里将新增的节点加在叶子结点上
(2)递归:
1)返回值为空,参数是当前节点和要添加的节点
2)边界:如果左节点为空且新增节点值大于cur的值,那么加到左节点上;如果右节点为空且新增节点值小于cur的值,那么加到右节点上
3)单层递归:
如果cur 的值大于新增节点值,则要去左子树中安置新节点;如果cur 的值大于新增节点值,则要去左子树中安置新节点
(3)改进的方法
1)边界条件改为判断cur是否为空,并为父节点添加左/右节点**(需要记录父节点)**
2)改为返回节点指针也可以更简单

(不过个人认为原始方法更直观一些)

3.题解

原始方法

class Solution {
public:
    void trace(TreeNode* cur, TreeNode* node) {
        if (cur->left == NULL && cur->val > node->val) {
            cur->left = node;
            return;
        }
        if (cur->right == NULL && cur->val < node->val) {
            cur->right = node;
            return;
        }
        if (cur->val > node->val)
            trace(cur->left, node);
        else
            trace(cur->right, node);
    }
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        TreeNode* node = new TreeNode(val);
        if (root == NULL)
            return node;
        trace(root, node);
        return root;
    }
};

改进边界条件

class Solution {
public:
    TreeNode* parent;
    void trace(TreeNode* cur, int val) {
        if (cur == NULL) {
            TreeNode* node = new TreeNode(val);
            if (parent->val > val)
                parent->left = node;
            else
                parent->right = node;
            return;
        }
        parent = cur;

        if (cur->val > val)
            trace(cur->left, val);
        else
            trace(cur->right, val);
    }
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        if (root == NULL) {
            TreeNode* node = new TreeNode(val);
            return node;
        }

        trace(root, val);
        return root;
    }
};

改进返回值

class Solution {
public:
    TreeNode* insertIntoBST(TreeNode* root, int val) {

        if (root == NULL) {
            TreeNode* node = new TreeNode(val);
            return node;
        }
        if (root->val > val)
            root->left = insertIntoBST(root->left, val);
        else
            root->right = insertIntoBST(root->right, val);
        return root;
    }
};

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

1.题目链接

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

2.思路

(1)参数和返回值:传入根节点Root(相当于当前节点)和目标key,返回当前子树根节点
好像返回节点比void简单,因为不需要记录父节点
(2)边界条件:如果遍历到空节点就返回
(3)单层递归
五种情况处理:
1)找到删除节点,并且删除节点为叶子结点:删除节点变为NULL,返回当前节点

2)找到删除节点,并且删除节点左子节点为空,右子节点不为空:删除节点由右子节点顶替

3)找到删除节点,并且删除节点左子节点不为空,右子节点为空:删除节点由左子节点顶替

4)找到删除节点,并且删除节点左子节点不为空,右子节点不为空:
这里的做法是:删除节点的左子结点作为删除节点右子树中最左面节点的左子结点,再由右子节点补位,返回右子节点

为什么
①删除节点由右子节点或左子节点顶替均可,这里以删除节点由右子节点顶替为例,重点关注没有顶替删除节点的那个子节点
②右子树不动,右子树中最小的节点是最左面的节点(暂且叫做A),A也是比删除节点的左子结点的值大的,所以删除节点的左子节点只有成为A的左子结点,才能确保自己左边没有更大的节点

3.题解

class Solution {
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        if (root == NULL)
            return root;
        if (root->val == key) {
            TreeNode* tmp=root;
            if (root->left == NULL && root->right == NULL)
                root = NULL;
            else if (root->left == NULL && root->right != NULL)
                root = root->right;
            else if (root->left != NULL && root->right == NULL)
                root = root->left;
            else {
                TreeNode* min_in_right = root->right;
                while (min_in_right->left != NULL) {
                    min_in_right = min_in_right->left;
                }
                min_in_right->left = root->left;
                root = root->right;
            }
            delete tmp;
            return root;
        }
        if (root->val > key)
            root->left = deleteNode(root->left, key);
        else
            root->right = deleteNode(root->right, key);
        return root;
    }
};

总结

今天最难的还是删除二叉搜索树节点

;