【力扣一刷】代码随想录day22(235. 二叉搜索树的最近公共祖先、701.二叉搜索树中的插入操作、450.删除二叉搜索树中的节点 )

目录

【235. 二叉搜索树的最近公共祖先】中等题

方法一  递归 - 后序遍历(普通二叉树的解法)

方法二  递归 - 前序遍历(针对二叉搜索树性质的解法)

方法三  统一迭代法 - 前序迭代(针对二叉搜索树性质的解法)

【701.二叉搜索树中的插入操作】中等题

方法一  递归

方法二  迭代法

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


【235. 二叉搜索树的最近公共祖先】中等题

方法一  递归 - 后序遍历(普通二叉树的解法)

思路:

1、如果当前节点为null,或者当前节点为p或q,直接返回当前节点。

如果当前节点是null,直接返回null即可。

如果当前节点是p,q不在在当前节点的子树上,证明找到了p,返回p即可。

如果当前节点是q,p不在在当前节点的子树上,证明找到了q,返回q即可。

如果当前节点是p,q在当前节点的子树上,那么最近公共祖先就是p,返回p即可。

如果当前节点是q,p在当前节点的子树上,那么最近公共祖先就是q,返回q即可。
 

2、获取左右子树的搜索结果

如果搜索结果都不为空,证明p、q分别存在当前节点的左右子树中,返回当前节点。

如果搜索结果都为空,证明p、q都不在当前节点左右子树中,返回null。

如果其中一个搜索结果不为空,则返回不为空的搜索结果。

关键:自底向上将搜索结果往上传

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null || root == p || root == q) return root;

        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        if (left != null && right != null) return root;
        if (left == null && right == null) return null;
        else if (left != null) return left;
        else return right;
    }
}

方法二  递归 - 前序遍历(针对二叉搜索树性质的解法)

思路:

对于二叉搜索树,当前节点的val均大于左子树节点的val,均小于右子树节点的val。

  • 如果当前节点的val值在(p, q)或(q, p)之间,那么该节点就是最近公共祖先;
  • 如果当前节点的val值均大于/小于p、q,则p、q的最近公共祖先肯定出现在当前节点的左子树/右子树上。

区别:

二叉搜索树可以通过当前节点的val和p、q节点的val判断当前节点和p、q节点的位置关系;

而普通二叉树需要通过自底向上回溯的方式判断左右子树中是否包含p、q节点。

原始版

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null || root == p || root == q) return root;  // 避免自己是最近公共祖先的情况

        // 如果当前节点在(p,q)或(q,p)之间,直接返回当前节点
        if ((root.val > p.val && root.val < q.val) || (root.val > q.val && root.val < p.val)) return root;
        // 如果当前节点大于p和q,证明最近公共祖先会出现在当前节点的左子树,返回左子树的搜寻结果
        else if (root.val > p.val && root.val > q.val) return lowestCommonAncestor(root.left, p, q);
        // 如果当前节点小于p和q,证明最近公共祖先会出现在当前节点的右子树,返回右子树的搜寻结果
        else return lowestCommonAncestor(root.right, p, q);
    }
}

简洁版

思路:在到达叶子节点前,最近公共祖先就会被找到,因此不存在root=null的情况,可以直接访问root.val。因此,直接返回root的情况可以合并到最后。

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root.val > p.val && root.val > q.val) return lowestCommonAncestor(root.left, p, q);
        // 如果当前节点小于p和q,证明最近公共祖先会出现在当前节点的右子树,返回右子树的搜寻结果
        if (root.val < p.val && root.val < q.val) return lowestCommonAncestor(root.right, p, q);
        // 其余情况,包含当前节点= p或q,也包含当前节点在(p,q)或(q,p)区间内,直接返回root
        return root;
    }
}

方法三  统一迭代法 - 前序迭代(针对二叉搜索树性质的解法)

思路:

1、统一迭代法的前序迭代入栈顺序【cur.right -> cur.left -> cur -> null】

2、处理节点时,

  • 判断当前节点是否为p、q,是则直接返回当前节点。(原因:这种情况只可能是自己就是最近公共祖先,不存在越过了最近公共祖先再访问到其中一个节点p/q的情况,因为下一步就是判断当前节点是否在区间内)
  • 判断当前节点是否在(q, p)或(p, q)区间内,是则直接返回当前节点。
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        Deque<TreeNode> stack = new LinkedList<>();
        TreeNode cur = root;
        stack.push(cur);

        while (!stack.isEmpty()){
            cur = stack.pop();
            if (cur != null){
                if (cur.right != null) stack.push(cur.right);
                if (cur.left != null) stack.push(cur.left);
                stack.push(cur);
                stack.push(null);
            }
            else{
                cur = stack.pop();
                if (cur == p || cur == q) return cur;
                if ((cur.val > q.val && cur.val < p.val) || (cur.val > p.val && cur.val < q.val)) return cur;
            }
        }
        return null;
    }
}


【701.二叉搜索树中的插入操作】中等题

方法一  递归

思路:

  • 如果root.val > val,说明应该将val插入左子树
    • 如果左子树不存在,将val作为root.left
    • 如果左子树存在,让左子树插入val
  • 如果root.val < val,说明应该将val插在右子树
    • 如果右子树不存在,将val作为root.right
    • 如果右子树存在,让右子树插入val
  • 返回插入val后的根节点

自己想的

class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
        if (root == null) return new TreeNode(val);

        if (root.val > val) {
            if (root.left == null) root.left = new TreeNode(val);
            else insertIntoBST(root.left, val);
        }
        // 其余情况只可能是root.val > val,因为题目说明“保证 val 在原始BST中不存在”
        else { 
            if (root.right == null) root.right = new TreeNode(val);
            else insertIntoBST(root.right, val);
        }
        return root;
    }
}

简洁版

思路:直接获取左右子树插入节点后的根节点即可。

class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
        if (root == null) return new TreeNode(val);
        // 如果root.val > val,将val插入到左子树,获取插入val后左子树的根节点
        if (root.val > val) root.left = insertIntoBST(root.left, val);
        // 如果root.val < val,将val插入到右子树,获取插入val后右子树的根节点
        else root.right = insertIntoBST(root.right, val);

        // 返回插入val后的根节点
        return root;
    }
}

方法二  迭代法

思路:

1、如果root为空,直接插入后返回

2、利用while循环,找到对应位置的空位进行插入val,插入后结束while循环

3、返回插入val后的根节点root

class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
        if (root == null) return new TreeNode(val);  // root为空,直接插入后返回

        TreeNode cur = root;
        while (cur != null){
            if (cur.val > val) {
                // cur.val > val,证明应该将val插入到左子树
                if (cur.left == null) {
                    cur.left = new TreeNode(val);
                    break; // 插入节点后任务结束,结束while循环
                }
                else cur = cur.left;
            }
            else {
                // cur.val < val,证明应该将val插入到右子树
                if (cur.right == null) {
                    cur.right = new TreeNode(val);
                    break; // 插入节点后任务结束,结束while循环
                }
                else cur = cur.right;
            }
        }
        return root;
    }
}


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

二叉搜素树的性质:

左子树的所有val值 <  当前节点的val值 < 右子树的最小val值 = 右子树的最左边节点的val值

思路:

箭头表示中序遍历(升序)过程,当前节点的val为key的时候(假设 key = 7),将左子树 [4, 5, 6] 拼接到右子树的最左边的节点 [8] 上 ,再用右节点 [9] 替代当前节点 [7] 实现删除。

删除前:

删除后:

步骤:

  • 如果当前节点为空,就删不了,直接返回nul
  • 如果root.val = key,证明当前节点就是要删除的节点
    • 如果当前节点是叶子节点,直接删除了,返回null即可
    • 如果左子树为空,直接返回右子树即可
    • 如果右子树为空,则直接返回左子树
    • 如果左右子树都不为空,寻找右子树最左边的节点,将左子树拼接到右子树最左边的节点,返回当前节点的右节点,作为删除后树的根节点
  • 如果root.val > key,证明要删除的节点应该会出现在左子树上,获取左子树删除key节点后的根节点作为新的左节点
  • 如果root.val < key,证明要删除的节点应该会出现在右子树上,获取右子树删除key节点后的根节点作为新的右节点
  • 返回删除key节点后的根节点
class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        // 如果当前节点为空,就删不了,直接返回null
        if (root == null) return root;

        if (root.val == key){
            // 如果当前节点是叶子节点,直接删除了,返回null即可
            // if (root.left == null && root.right == null) return null;  // 可以合并到左子树为空的情况

            // 如果左子树为空,直接返回右子树即可
            if (root.left == null) return root.right;

            // 如果右子树为空,则直接返回左子树
            if (root.right == null) return root.left;

            // 如果左右子树都不为空,寻找右子树最左边的节点,将左子树拼接到右子树最左边的节点
            TreeNode tmp = root.right; // 用于指向右子树最左边的节点
            while (tmp.left != null){
                tmp = tmp.left;
            }
            tmp.left = root.left; // 将左子树拼接到右子树最左边的节点
            return root.right; // 返回当前节点的右节点,作为删除后树的根节点
        }
        // 如果root.val > key,证明要删除的节点应该会出现在左子树上,获取左子树删除key节点后的根节点作为新的左节点
        else if (root.val > key) {
            root.left = deleteNode(root.left, key);
        }
        // 如果root.val < key,证明要删除的节点应该会出现在右子树上,获取右子树删除key节点后的根节点作为新的右节点
        else{
            root.right = deleteNode(root.right, key);
        }
        // 返回删除key节点后的根节点
        return root;
    }
}

  • 8
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值