Day31--贪心--56. 合并区间,738. 单调递增的数字,968. 监控二叉树

Day31–贪心–56. 合并区间,738. 单调递增的数字,968. 监控二叉树

本文精华在《968. 监控二叉树》的讲解,非常生动易懂。

56. 合并区间

方法:贪心

思路:

  1. Arrays.srot(),使用Lambda表达式指定规则。(使用Integer.compare(x, y)安全比较,避免溢出)
  2. 《重叠区间》问题的常规套路
  3. 用Stream将二维列表转换为二维数组
class Solution {
    public int[][] merge(int[][] intervals) {
        // 结果集
        List<List<Integer>> res = new ArrayList<>();
        // 按左边界从小到大排序(使用Integer.compare(x, y)安全比较,避免溢出)
        Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0]));
        // 初始化左右边界为第一个区间
        int left = intervals[0][0];
        int right = intervals[0][1];
        // 从第二个区间开始遍历(与前一个区间的关系)
        for (int i = 1; i < intervals.length; i++) {
            // 重叠了,更新右边界,右边界为max(前一区间右边界,当前区间右边界)
            // 就是要找到最长的“尾巴”,才能把这两个区间“包进去”
            if (right >= intervals[i][0]) {
                right = Math.max(right, intervals[i][1]);
            } else {
                // 不重叠,把前面的区间放到结果集
                List<Integer> list = new ArrayList<>();
                list.add(left);
                list.add(right);
                res.add(list);
                // 更新左右边界为当前的区间
                left = intervals[i][0];
                right = intervals[i][1];
            }
        }
        // 处理最后一个区间
        List<Integer> list = new ArrayList<>();
        list.add(left);
        list.add(right);
        res.add(list);
        // 将二维列表转成二维数组返回
        return res.stream()
                .map(innerList -> innerList.stream()
                        .mapToInt(Integer::intValue)
                        .toArray())
                .toArray(int[][]::new);
    }
}

738. 单调递增的数字

方法:贪心

思路:

  1. 如果不单调递增,找到要“减一”的那一位,后面的全部变9。
  2. 注意处理相同的数的情况。比如332,要减一的位置不是第二个3,而是第一个3。
  3. 注意注意处理首位是0的情况
// 如果不合适,找到要“减一”的那一位,后面的全部变9
class Solution {
    public int monotoneIncreasingDigits(int n) {
        // 转换成数组
        String s = "" + n;
        int[] arr = new int[s.length()];
        for (int i = 0; i < s.length(); i++) {
            arr[i] = s.charAt(i) - '0';
        }

        // 开始从前向后遍历,两位一组看,当不符合单调递增的时候退出循环
        int i = 1;
        for (; i < arr.length; i++) {
            if (arr[i - 1] > arr[i]) {
                i = i - 1;
                break;
            }
        }
        // 非正常退出,证明break了
        if (i != arr.length) {
            // 往前找到要“减一”的位置。如果是严格大于的话,不会进这个循环。
            // 这个while处理相同的数的情况。比如332,要减一的位置不是第二个3,而是第一个3
            while (i >= 1 && arr[i - 1] == arr[i]) {
                i--;
            }
            // 当前位置减一,变成232
            arr[i]--;
            // 后一位开始,到末尾,全部变成9.也就是299
            i++;
            while (i < arr.length) {
                arr[i++] = 9;
            }
        }
        // 数组求和,注意处理首位是0的情况
        int j = 0;
        if (arr[0] == 0) {
            j = 1;
        }
        int sum = 0;
        for (; j < arr.length; j++) {
            sum = sum * 10 + arr[j];
        }
        return sum;
    }
}

思路:

  1. 从右向左遍历,就简单多了。找到第一个需要“减一”的地方,后续全部变成9
  2. 注意,这里parseInt()方法, 自动处理了前导零的情情况。比如"09"输出9
// 从右向左遍历,就简单多了
class Solution {
    public int monotoneIncreasingDigits(int n) {
        String s = String.valueOf(n);
        char[] ch = s.toCharArray();
        int start = s.length();
        for (int i = s.length() - 2; i >= 0; i--) {
            if (ch[i] > ch[i + 1]) {
                ch[i]--;
                start = i + 1;
            }
        }
        for (int i = start; i < s.length(); i++) {
            ch[i] = '9';
        }
        // 注意,这里parseInt()方法, 自动处理了前导零的情情况。比如"09"输出9
        return Integer.parseInt(String.valueOf(ch));
    }
}

968. 监控二叉树

方法:贪心

思路:

  1. 后续遍历,往上传递信息:
  2. 三个状态,1,可以提供帮助,0不需要管我,-1,需要帮助
  3. if的顺序不能变。要优先考虑儿子的情况,再到自己。
  4. 最后别忘了处理根的情况,当“根”说自己想要被老爸管,它是没老爸的,要自己装

最能体现贪心的地方是:“都不需要管,我也不管自己了,找老爸管”

//  两个状态用boolean,三个状态要用int
class Solution {

    // 计数
    int count = 0;

    public int minCameraCover(TreeNode root) {
        int condition = postorderTravel(root);
        // 最后别忘了处理根的情况,当“根”说自己想要被老爸管,它是没老爸的,要自己装
        if (condition == -1) {
            count++;
        }
        return count;
    }

    // 三个状态,1,可以提供帮助,0不需要管我,-1,需要帮助
    private int postorderTravel(TreeNode node) {

        // 后续遍历左右根
        int left = 0;
        int right = 0;

        // 左
        if (node.left != null) {
            left = postorderTravel(node.left);
        }

        // 右
        if (node.right != null) {
            right = postorderTravel(node.right);
        }

        // 根,开始处理情况
        // 有一方需要管,也要照顾儿子,装灯
        if (left == -1 || right == -1) {
            count++;
            return 1;
        }

        if (left == 0 && right == 0) {
            // 都不需要管,我也不管自己了,找老爸管
            return -1;
        }

        // 有儿子管我了,不需要自己装,也不需要老爸装。
        if (left == 1 || right == 1) {
            return 0;
        }
        // 注意,上面三个if的顺序不能变。要优先考虑儿子的情况,再到自己。
        return 0;
    }
}

贪心总结

很多贪心题,做完了之后根本没发现自己用了贪心的方法。可能我本来就足够贪心吧。哈哈哈。

再来念一遍贪心的思路:找出局部最优,并可以推出全局最优。

题目分类:

  • 两个维度维权问题
    • 分发糖果
    • 根据身高重建队列
  • 重叠区间问题
    • 跳跃游戏
    • 引爆气球
    • 无重叠区间
    • 划分字母区间
    • 合并区间
  • 其他
    • 加油站
    • 摆动序列
    • 最大子数组和
    • 监控二叉树
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值