【代码训练营】day 37 | 738.单调递增的数字 & 714. 买卖股票的最佳时机含手续费 & 968.监控二叉树

所用代码 java

单调递增的数字 LeetCode 738

题目链接:单调递增的数字 LeetCode 738 - 中等

思路

无。题都没看懂


题目的意思是:

给一个数如:123 
每个位数的数字是 1 2 3 
这三个数字是单调递增的,所以返回小于或等于123的单调递增的数字为 123
​
如给一个数 10 ,每个数字为 1 0 这就不是单调递增的,返回的小于或等于10的单调递增的数字为 9

给定一个数如32,若该数不是单调递增的就把前一位数减1,后一位取最大值9,就是29,该题就是单递增的。

第二个需注意的就是从后往前遍历较为方便。

class Solution {
    public int monotoneIncreasingDigits(int n) {
        // 先转为char数组方便对每一位数处理
        String str = String.valueOf(n);
        char[] c = str.toCharArray();
        // flag标记之后,把后面的数都置为9
        int flag = c.length;
        // 从后往前遍历,可减少比较次数
        for (int i = c.length - 1; i > 0 ; i--) {
            // 非单调递增就让该数-1,并记录标记位
            if (c[i-1] > c[i]){
                c[i-1]--;
                flag = i;
            }
        }
        for (int i = flag; i < c.length; i++) {
            c[i] = '9';
        }
        // char数组转int
        return Integer.parseInt(String.valueOf(c));
    }
}

总结

本题有几个重点:

  • 为什么需要flag:若数是 1000,最大单调递增则是999,没有flag直接赋值的话,千位的 1<0,1变成0,0变成9,其他位不变,该数就变成了900。
  • int flag = c.length; 等于数组长度是为了防止原数组本来就是单调递增的,又重新去赋值了

本题可以暴力,但是超时了!:

class Solution {
    public int monotoneIncreasingDigits(int n) {
        // 从大到小遍历
        for (int i = n; i >= 0; i--) {
            if (checkNum(i)) return i;
        }
        return -1;
    }// 判断是否是单调递增
    public boolean checkNum(int num){
        int max = 10;
        while (num > 0){
            int t = num % 10;
            if (max >= t) max = t;
            else return false;
            num /= 10;
        }
        return true;
    }
}

买股票的最佳时机含手续费 LeetCode 714

题目链接:买股票的最佳时机含手续费 LeetCode 714 - 中等

思路

无。


class Solution {
    public int maxProfit(int[] prices, int fee) {
        int sum = 0;
        int minPrice = prices[0];
        for (int i = 1; i < prices.length; i++) {
            // 买入
            if (prices[i] < minPrice) {
                minPrice = prices[i];
            }// 此时卖出并不赚钱
            if (prices[i] >= minPrice && prices[i] <= minPrice + fee){
                continue;
            }// 有利润则卖出,实际为继续持股,后面继续收集利润
            if (prices[i] > minPrice + fee){
                sum += prices[i] - minPrice - fee;
                // 防止多减了一次利润 补的一个值
                minPrice = prices[i] - fee;
            }
        }
        return sum;
    }
}

总结

重点在于为什么会有这一步: minPrice = prices[i] - fee;

若prices[1,7,8] fee=2,我们在price=7的时候就卖出 sum= 7 - 1- 2 = 4,但此时并不是最佳的利润,最佳利润应该是price=8时,sum = 8 - 1 - 2 = 5。

所以当我们在price=7时,获得的利润并不是真正的利润,但是已经加了一次利润了,所以在第二次更新利润的时候不能再减去手续费了,即求和利润中的 prices[i+1] - minPrice - fee 需将之前的最小利润minPrice替换为minPrice = prices[i] - fee 带入第一个式子即:prices[i+1] - (prices[i] - fee) - fee = prices[i+1] - prices[i]

所以当我们在price=7获得了一次利润sum= 7 - 1- 2 = 4,然后在利润更高的点price=8更新一次更高的利润和,即后一天价格减前一个价格 prices[i+1] - prices[i]

监控二叉树 LeetCode 968

题目链接:监控二叉树 LeetCode 968 - 困难

思路

无。


一个摄像头可以覆盖上中下,首先要考虑叶子结点的摄像头,需叶子结点的父节点去放,就可以考虑多个,而叶子结点的话就应使用后序(左右中)。

难点在于隔一个结点放摄像头,我们把结点状态分为三种:

  • 0 无覆盖
  • 1 有摄像头
  • 2 有覆盖 – 空结点有覆盖

所以我们可以分为四种情况:

  1. 左右孩子都有覆盖,父节点无覆盖
  2. 左右还是至少有一个无覆盖,父节点必装摄像头
  3. 左右孩子至少有一个有摄像头,父节点有覆盖
  4. 根结点为无覆盖状态,根结点需放一个摄像头
class Solution {
    int result;
    public int minCameraCover(TreeNode root) {
        result = 0;
        if (traversal(root) == 0) result++;
        return result;
    }public int traversal(TreeNode node){
        // 空结点默认为有覆盖
        if (node == null) return 2;int left = traversal(node.left);
        int right = traversal(node.right);// 1、左右都有覆盖的话,那么本结点都是无覆盖
        if (left == 2 && right == 2) return 0;// 2、左右结点其中一个无覆盖的话,应该装摄像头
        if (left == 0 || right == 0) {
            result++;
            return 1;
        }// 3、左右结点其中一个有摄像头的话,本结点就是有覆盖
        if (left == 1 || right == 1) return 2;
        
        // 这个结果不会走,上面已经包括完了
        return -999;
    }
}

总结

本题其中有一个重点就是,按这个思路不是if else的话,顺序不能出错,必须按个按照上面的顺序。中结点的处理顺序为:

  1. 先判断左右是不是都有覆盖(叶子结点)
  2. 若出现根结点左边一个叶子结点,右边两个这种情况,根节点左右就是一个无覆盖一个装了摄像头,必须按这个顺序才会在根结点装一个摄像头,否则先走3再走2就被判断有覆盖会漏掉一个。
  3. 最后才是左右其中一个有摄像头。

注意: 上面三种情况已囊括了返回值为:0 1 2的所有情况,不会最后走到返回-999的情况了。

你可能感兴趣的:(代码训练营,leetcode,算法,贪心算法,数据结构,java)