二叉树的最紧公共祖先

530.二叉搜索树的最小绝对差

题目链接/文章讲解:https://programmercarl.com/0530.%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E7%9A%84%E6%9C%80%E5%B0%8F%E7%BB%9D%E5%AF%B9%E5%B7%AE.html

视频讲解:https://www.bilibili.com/video/BV1DD4y11779

本题思路和98.验证二叉搜索树类似,利用二叉搜索树的中序遍历递增的特性

class Solution {
    // 记录前一个节点
    TreeNode per = null;
    int Min = Integer.MAX_VALUE;
    public int getMinimumDifference(TreeNode root) {
        if(root == null) return 0;

        getMinimumDifference(root.left); // 左
        if(per!=null && (root.val-per.val)<Min){ //中
            Min = root.val-per.val;
        }
        // 第一次per为空,per就会记录叶子节点
        // 然后回退到父节点,那么per就是当前节点的前一个节点 
        per = root;
        getMinimumDifference(root.right); // 右

        return Min;
    }
}

501.二叉搜索数中的众数

https://programmercarl.com/0501.%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E4%B8%AD%E7%9A%84%E4%BC%97%E6%95%B0.html

视频讲解:https://www.bilibili.com/video/BV1fD4y117gp

// 解法一
class Solution {
    public int[] findMode(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        Map<Integer,Integer> map = new HashMap<>();
        if(root == null) return new int[]{};
        // 二叉树转换成有序集合
        find(root,res);
        // 将有序集合存储到map中[值,出现的次数]
        for(int num:res){
           map.put(num,map.getOrDefault(num,0)+1);
        }

        int maxNum = Integer.MIN_VALUE;
        // 遍历Map集合,找出众数
        for(Map.Entry<Integer,Integer> enter : map.entrySet()){
            // 当前次数 == 最大次数:直接存入结res集合
            if(enter.getValue() == maxNum){
                res.add(enter.getKey());
            }
            // 当前次数 > 最大次数
            // 那么就更新maxNum,并且清空res集合
            // 因为有新的值大于最大次数了,所以不仅要更新maxNum,还要清空掉之前存储的结果
            // 再将新的符合要求的值存入
            if(enter.getValue() > maxNum){
                maxNum = enter.getValue();
                res.clear();
                res.add(enter.getKey());
            }
            
        }
        // 将集合转换成数组
        int[] arr = new int[res.size()];
        for(int i=0; i<res.size(); i++){
            arr[i] = res.get(i);
        }
        return arr;
    }

    // 利用中序遍历把二叉树存入集合中
    public void find(TreeNode cur,List<Integer> res){
        if(cur == null) return ;

        find(cur.left,res);
        res.add(cur.val);
        find(cur.right,res);
    }
}
// 解法二
class Solution {
    TreeNode per = null; // 记录前一个节点
    int maxCount = 1; // 最大次数
    int count = 0; // 当前值出现次数
    List<Integer> res = new ArrayList<>(); // 结果集
    public int[] findMode(TreeNode root) {
        find(root);

        int[] arr = new int[res.size()];
        for(int i=0; i<res.size(); i++){
            arr[i] = res.get(i);
        }
        return arr;
    }

    // 利用中序遍历把二叉树存入集合中
    public void find(TreeNode cur){
        if(cur == null) return ;

        find(cur.left); // 左
        // 遍历到叶子节点,就把当前值出现的次数初始化为1
        if(per == null) count=1;
        // 如果前一节点 == 当前节点,count就加一
        else if(per.val == cur.val) count++;
        // 否则表示cur是新的数值,就把count更新为1
        else count = 1;
        per=cur;
        // 收集结果
        // 当前值次数 == 最大次数,就把当前值存入结果集
        if(count == maxCount) res.add(cur.val);
        // 当前值次数 > 最大次数,那么更新maxCount,并且清空res集合,再将新的值存入
        // 因为最大次数,并不是遍历完一次二叉树后找出来的,是在遍历的过程中不断更新的
        // 所以当有新值的出现次数>最大次数时
        // 不止要更新最大次数,还要清空掉存储的结果,因为都不符合要求了,再将新的符合要求的值存入
        if(count > maxCount){
            maxCount = count;
            res.clear();
            res.add(cur.val);
        }
        find(cur.right); // 右
    }
}

236.二叉树的最近公共祖先

https://programmercarl.com/0236.%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E6%9C%80%E8%BF%91%E5%85%AC%E5%85%B1%E7%A5%96%E5%85%88.html

视频讲解:https://www.bilibili.com/video/BV1jd4y1B7E2

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

题目分析:

  1. 满足什么条件才算公共祖先?

    • 情况一:p和q,当前节点的左孩子存在p,右孩子存在q,那么当前节点就是pq节点的公共祖先
    • 情况二:p和q1,此时直接就可以把q1返回回去,因为当前q1就是公共祖先
      • 为什么可以不去判断p直接返回q1呢?
      • 因为题目要求p和q1是一定会存在,不会有只存在一个节点的情况
      • 那么如果p不在q1的左右孩子里呢?
      • 如果q1的左右孩子不存在p,那么我们不就是返回到了情况一的步骤里面了嘛
  2. 既然查找公共祖先,我们肯定就要自底向上进行查找,那应该用什么遍历顺序?

    • 既然是自底向上查找,那么我们就一定是使用后序遍历,向上查找那么就记录二叉树的高度,高度就是后序遍历
  3. 注意:遍历二叉树一定是要把整个二叉树都遍历一遍,

    • 可能如图q1已经是公共祖先,但是我们还是要查找一遍根节点的右孩子

    • 因为我们是根据左右节点的返回值来处理中间节点

class Solution {
    // 返回值:因为我们要知道左右孩子中是否存在pq节点,所以我们就要不断返回节点值
    // 参数:要不断传入当前节点和pq目标节点
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        // 终止条件:查找到了pq节点就开始返回
        if(root == null) return null;
        if(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;
        // 只查找到一边存在p或者q,那么就向上返回查找到的哪一个
        // 直到某个的节点的另一边也查找到另外一个节点,那么这个节点就是公共祖先
        if(left==null && right!=null) return right;
        if(left!=null && right==null) return left;
        return null;
        
    }
}

直到某个的节点的另一边也查找到另外一个节点,那么这个节点就是公共祖先
if(leftnull && right!=null) return right;
if(left!=null && right
null) return left;
return null;

}

}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值