代码随想录算法训练营第37天 | 738.单调递增的数字 ,968.监控二叉树

贪心算法章节理论基础:

https://programmercarl.com/%E8%B4%AA%E5%BF%83%E7%AE%97%E6%B3%95%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html

738.单调递增的数字

题目链接:https://leetcode.cn/problems/monotone-increasing-digits/

思路:

题目要求小于等于N的最大单调递增的整数,从后向前遍历,比如322来说,332 -> 329 -> 299。首先当前数字arr[i]–,然后最后一位给9。

要注意一个点就是,不能直接赋值成9,这里用的是一个start来保存位置,因为比如100,按我们刚刚的算法,他的结果是9,而不是99了。要注意一下这个地方。

class Solution {
    public int monotoneIncreasingDigits(int n) {
        // 从最后一位开始
        char[] arr = String.valueOf(n).toCharArray();
        int start = arr.length;
        for(int i=arr.length-2;i>=0;i--){
            if(arr[i] > arr[i + 1]){
                // arr[i+1] = '9';
                start = i + 1;
                arr[i]--;   // 当前位置减少1
            }
        }
        // 100 -> 99
        for(int i=start;i < arr.length;i++){
            arr[i] = '9';
        }
        return Integer.parseInt(String.valueOf(arr));
    }
}

968.监控二叉树

题目链接:https://leetcode.cn/problems/binary-tree-cameras/description/

思路:

摄像头可以覆盖上中下三层,如果把摄像头放在叶子节点上,就浪费的一层的覆盖。

所以我们要从下往上看,局部最优:让叶子节点的父节点安摄像头,所用摄像头最少,整体最优:全部摄像头数量所用最少!

可以使用后序遍历也就是左右中的顺序,这样就可以在回溯的过程中从下到上进行推导了。

下面,来看看这个状态应该如何转移,先来看看每个节点可能有几种状态:

有如下三种:

  • 该节点无覆盖
  • 本节点有摄像头
  • 本节点有覆盖

我们分别有三个数字来表示:

0:该节点无覆盖
1:本节点有摄像头
2:本节点有覆盖

为了让摄像头数量最少,我们要尽量让叶子节点的父节点安装摄像头,这样才能摄像头的数量最少。

因为在遍历树的过程中,就会遇到空节点,那么问题来了,空节点究竟是哪一种状态呢? 空节点表示无覆盖? 表示有摄像头?还是有覆盖呢?

空节点不能是无覆盖的状态,这样叶子节点就要放摄像头了,空节点也不能是有摄像头的状态,这样叶子节点的父节点就没有必要放摄像头了,所以空节点的状态只能是有覆盖,这样就可以在叶子节点的父节点放摄像头了。

那么递归的终止条件应该是遇到了空节点,此时应该返回2(有覆盖)。

// 空节点,该节点有覆盖
if (cur == NULL) return 2;

主要有如下四类情况:

情况1:左右节点都有覆盖
左孩子有覆盖,右孩子有覆盖,那么此时中间节点应该就是无覆盖的状态了。

情况2:左右节点至少有一个无覆盖的情况
如果是以下情况,则中间节点(父节点)应该放摄像头.

情况3:左右节点至少有一个有摄像头
如果是以下情况,其实就是 左右孩子节点有一个有摄像头了,那么其父节点就应该是2(覆盖的状态).

情况4:头结点没有覆盖
以上都处理完了,递归结束之后,可能头结点 还有一个无覆盖的情况。
在这里插入图片描述
所以递归结束之后,还要判断根节点,如果没有覆盖,result++。

class Solution {
    public List<Integer> partitionLabels(String s) {
        int[] arr = new int[26];
        int len = s.length();
        // 统计每一个字符最后出现的位置
        for(int i = 0;i<len;i++){
            char c = s.charAt(i);
            arr[c-'a'] = i+1;
        }
        int left = 0;
        int right = 0;
        List<Integer> list = new LinkedList<>();
        for(int i=0;i<len;i++){
            // 找到字符最远的右边界
            right = Math.max(right,arr[s.charAt(i)-'a']);   
            if(right == i+1){
                list.add(right - left);
                left = right;
            }
        }
        return list;
    }
}
  • 25
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值