代码随想录算法训练营第三十七天 | Java |56. 合并区间、738.单调递增的数字、968.监控二叉树【贪心算法】

56. 合并区间 

题目:以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。

提示:

  • 1 <= intervals.length <= 104
  • intervals[i].length == 2
  • 0 <= starti <= endi <= 104

思路:本题与452,435相类似,都是对比区间数组,然后在此基础上修改区间范围。本题需要借助一个辅助集合记录新的区间数组。首先对区间数组进行排序,分别记录当前区间的起始和结束位置。当两个数组的区间有重叠时,修改记录的范围,直到新的数组不和当前记录重叠。将记录添加到辅助数组中。

注意:记录范围的变化,一定是取最大范围;区间数组在判断循环结束后,需要将最后一次记录的数据保存到辅助数组中。

Java实现

class Solution {
    public int[][] merge(int[][] intervals) {
        // 辅助数组保存新的区间数组
        List<int[]> res = new LinkedList<>();

        // 首先对数组进行排序,然后逐一比较重叠区间,将新区间加入到res中
        Arrays.sort(intervals,(a,b)->Integer.compare(a[0],b[0]));

        int start = intervals[0][0];
        int end = intervals[0][1];
        for(int i=1; i<intervals.length; i++){
            if(intervals[i][0]<=end ){
                end = Math.max(intervals[i][1],end);
            }else{
                res.add(new int[]{start,end});
                start = intervals[i][0];
                end = intervals[i][1];
            }
        }
        
        // 剩余的最后一个区间数组添加到集合中
        res.add(new int[]{start,end});
        return res.toArray(new int[res.size()][]);
    }
}

738. 单调递增的数字 

题目:当且仅当每个相邻位数上的数字 x 和 y 满足 x <= y 时,我们称这个整数是单调递增的。

给定一个整数 n ,返回 小于或等于 n 的最大数字,且数字呈 单调递增 。

提示:

  • 0 <= n <= 109

思路: 暴力遍历可以解答但是时间复杂度一定不满足要求。考虑到本题需要对每一位上的数进行比较,因此,将数首先转化为个位数的数组。其次,如果数组从前向后进行比较,那么每次修改后都要从头重新比较,因此,选择从末尾到开头遍历。现在开始考虑,如98,8<9即n[i]<n[i-1],此时对n[i-1]-1,将n[i]变为9,则此时一定满足n[i]>n[i-1]。再考虑如332,n[3]<n[2],则将332先变为329,仍不满足要求,此时n[2]<n[1],继续修改,此时为299,符合要求。更多位数的数以此类推。

注意:如332此类需要将多个数变为9,可以记录变9的位置,统一进行修改。

Java实现

class Solution {
    public int monotoneIncreasingDigits(int n) {
        String s = String.valueOf(n);
        char[] chars = s.toCharArray();
        int start = s.length();   // 标记变9的位置
        for(int i=s.length()-2; i>=0; i--){
            if(chars[i]>chars[i+1]){
                chars[i]--;
                start = i+1;
            }
        }
        for(int i=start; i<s.length(); i++){
            chars[i] = '9';
        }
        return Integer.parseInt(String.valueOf(chars));
    }
}

968. 监控二叉树 

题目:给定一个二叉树,我们在树的节点上安装摄像头。节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。计算监控树的所有节点所需的最小摄像头数量。

提示:

  1. 给定树的节点数的范围是 [1, 1000]
  2. 每个节点的值都是 0。

思路:本题很难,博主只有一个粗略的思路,更加详细的解析还需移步学习 代码随想录-968.监控二叉树 本题要考虑监控到所有的节点,如果从根节点从上至下遍历,需要随着树的形状不断该改变相机位置,因此考虑从叶子节点开始从下至上遍历。考虑到相机可以覆盖上下中三层,如果将相机置于叶子结点会浪费一层,因此,相机会置于叶子结点的父节点上,然后根据覆盖情况依次向上。具体的覆盖情况可以参考解析。

注意:存在多种覆盖情况。

Java实现

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    int res = 0;
    // 覆盖状态:0-无覆盖;1-相机;2-覆盖
    public int minCameraCover(TreeNode root) {
        // 对根节点状态进行检查,防止根节点无覆盖
        if(minCame(root)==0){
            res++;
        }
        return res;
    }

    private int minCame(TreeNode root){
        if(root==null){     // 叶子节点一定是被覆盖的
            return 2;
        }

        // 后续遍历,从下至上判断
        int left = minCame(root.left);
        int right = minCame(root.right);

        // 如果左右节点都覆盖了,那本节点状态未覆盖,且没有相机
        if(left==2 && right==2){
            return 0;
        }else if(left==0 || right==0){
            // 左右节点都是无覆盖状态,当前根节点应该设置相机
            res++;
            return 1;
        }else{
            return 2;
        }
        
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值