力扣刷题Day19

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

力扣链接:https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/

之前236二叉树的最近公共祖先这道题的回溯法一样可以给出答案。

但是这道题中是二叉搜索树 - 也就是是一颗有序的树。因此可以考虑采用二叉搜索树的特性来解决问题。

在有序树里,如果判断一个节点的左子树里有p,右子树里有q呢?

因为是有序树,所有 如果 中间节点是 q 和 p 的公共祖先,那么 中节点的数组 一定是在 [p, q]区间的。即 中节点 > p && 中节点 < q 或者 中节点 > q && 中节点 < p。

那么只要从上到下去遍历,遇到 cur节点是数值在[p, q]区间中则一定可以说明该节点cur就是q 和 p的公共祖先。

问题: 一定是最近公共祖先吗

我们从根节点搜索,第一次遇到 cur节点是数值在[p, q]区间中,即 节点5,此时可以说明 p 和 q 一定分别存在于 节点 5的左子树,和右子树中。

此时节点5是不是最近公共祖先? 如果 从节点5继续向左遍历,那么将错过成为q的祖先, 如果从节点5继续向右遍历则错过成为p的祖先。

所以当我们从上向下去递归遍历,第一次遇到 cur节点是数值在[p, q]区间中,那么cur就是 p和q的最近公共祖先。

因此可以采用递归法来解决这个问题:

递归三部曲:

  • 确定返回参数以及参数 - TreeNode; (TreeNode cur, TreeNode p, TreeNode q)

  • 确定终止条件 - 遇到空节点返回 (其实都不需要这个终止条件,因为题目中说了p、q 为不同节点且均存在于给定的二叉搜索树中。也就是说一定会找到公共祖先的,所以并不存在遇到空的情况。)

  • 确定单层递归逻辑 - 如果cur.var > p.var&q.var,说明公共祖先在左边 -- cur.left;如果cur.var < p.var&q.var,说明公共祖先在右边 -- cur.right;如果正好cur在中间,那么返回cur

注意:这里有返回值,所以需要采用if+return的方式进行递归

具体代码实现:

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
       return helper(root, p, q);
    }

    public TreeNode helper(TreeNode cur, TreeNode p, TreeNode q){
        if(cur == null){
            return cur;
        }

        if(cur.val > p.val && cur.val > q.val){
            TreeNode left = helper(cur.left, p, q);
            return left;
        }

        if(cur.val < p.val && cur.val < q.val){
            TreeNode right = helper(cur.right, p, q);
            return right;
        }

        return cur;
    }
}

参考资料:https://programmercarl.com/0235.二叉搜索树的最近公共祖先.html#递归法

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

力扣链接:https://leetcode.cn/problems/insert-into-a-binary-search-tree/

这道题不用给它想得太复杂(要去改变二叉树的结构之类的),直接根据二叉搜索树的特性,在叶子节点处插入二叉树就可以。

因此可以采用递归法进行实现:

递归三部曲:

  • 输出以及输入参数:TreeNode root; (TreeNode cur, int var)

  • 终止条件:if(cur = null) 插入节点并且返回

  • 单层循环:如果var比cur小,向左遍历;如果var比cur大,向右遍历。

注意:如何通过递归函数返回值完成了新加入节点的父子关系赋值操作了,下一层将加入节点返回,本层用root->left或者root->right将其接住

 if(root.val > val) root.left = insertIntoBST(root.left, val);
 if(root.val < val) root.right = insertIntoBST(root.right, val);

如果不用的话,那么返回的结果就不存在树的结构了。

具体代码实现:

 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);
        if(root.val < val) root.right = insertIntoBST(root.right, val);

        return root;

    }

参考资料:https://programmercarl.com/0701.二叉搜索树中的插入操作.html#递归

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

力扣链接:https://leetcode.cn/problems/delete-node-in-a-bst/

删除二叉搜索树的节点要比添加复杂很多。

因为有很多种情况:

  1. 没有找到要删除的节点。

  1. 删除的节点是叶子节点。

  1. 删除的节点有一个孩子。 1)左不空右为空;2)左为空右不空

  1. 删除的节点有两个孩子。

而第四种情况树最复杂的 - 左不为空右不为空。

  • 一种方法是:选择右孩子继承,且将左子树移动到右孩子中最左侧节点处。

  • 另一种方法是:找到删除节点中左子树中最右侧的叶子节点或者右子树中最左侧的叶子节点,然后将这个节点的值替换到删除节点中。然后直接删除这个叶子节点就可以了。

相对来说第二种方法在实现上面不改变树的高度,更优一些(实现上面稍微复杂一点,因为要多一次递归删除叶子节点)。

具体的代码实现:

public TreeNode deleteNode(TreeNode root, int key) {
       //条件1
       if(root == null) return root;

        //如果找到了这个节点 - 判断节点孩子空的情况
        if(root.val == key){
            //都为空
            if(root.left == null && root.right == null){
                return null;
            }
            
            if(root.left != null && root.right == null){
                return root.left;
            }

            if(root.left == null && root.right != null){
                return root.right;
            }
            //左右孩子都不为空
            if(root.left != null && root.right != null){
                //查找继承者
                TreeNode successor = root.right;
                while(successor.left != null){
                    successor = successor.left;
                }
                //替换值
                root.val = successor.val;
                
                //删除successor节点
                root.right = deleteNode(root.right, successor.val);

                return root;
            }

        }
        
        //重新构建树
        root.left = deleteNode(root.left, key);
        root.right = deleteNode(root.right, key);


        return root;
    }

参考资料:

https://programmercarl.com/0450.删除二叉搜索树中的节点.html#迭代法

https://leetcode.cn/problems/delete-node-in-a-bst/solutions/1529700/shan-chu-er-cha-sou-suo-shu-zhong-de-jie-n6vo/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,关于力扣刷题C++常用操作,我可以给你一些常见的操作和技巧: 1. 使用 STL 容器和算法库:STL(Standard Template Library)是 C++ 标准库中的一个重要组成部分,包含了许多常用的容器和算法。在力扣刷题中,使用 STL 可以大大提高代码的效率和可读性。例如,vector 可以用来存储动态数组,sort 可以用来排序等等。 2. 使用 auto 关键字:auto 关键字可以自动推导变量类型,可以减少代码量和提高可读性。例如,auto x = 1; 可以自动推导出 x 的类型为 int。 3. 使用 lambda 表达式:lambda 表达式是 C++11 中引入的一种匿名函数,可以方便地定义一些简单的函数对象。在力扣刷题中,使用 lambda 表达式可以简化代码,例如在 sort 函数中自定义比较函数。 4. 使用位运算:位运算是一种高效的运算方式,在力扣刷题中经常会用到。例如,左移运算符 << 可以用来计算 2 的幂次方,右移运算符 >> 可以用来除以 2 等等。 5. 使用递归:递归是一种常见的算法思想,在力扣刷题中也经常会用到。例如,二叉树的遍历、链表的反转等等。 6. 使用 STL 中的 priority_queue:priority_queue 是 STL 中的一个容器,可以用来实现堆。在力扣刷题中,使用 priority_queue 可以方便地实现一些需要维护最大值或最小值的算法。 7. 使用 STL 中的 unordered_map:unordered_map 是 STL 中的一个容器,可以用来实现哈希表。在力扣刷题中,使用 unordered_map 可以方便地实现一些需要快速查找和插入的算法。 8. 使用 STL 中的 string:string 是 STL 中的一个容器,可以用来存储字符串。在力扣刷题中,使用 string 可以方便地处理字符串相关的问题。 9. 注意边界条件:在力扣刷题中,边界条件往往是解决问题的关键。需要仔细分析题目,考虑各种边界情况,避免出现错误。 10. 注意时间复杂度:在力扣刷题中,时间复杂度往往是评判代码优劣的重要指标。需要仔细分析算法的时间复杂度,并尽可能优化代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值