代码随想录算法训练营第三十七天|738.单调递增的数字、714. 买卖股票的最佳时机含手续费、968.监控二叉树

32 阅读3分钟

738.单调递增的数字

题目链接:738. 单调递增的数字

思路:从后向前遍历,如果前一位大于当前位,就让前一位减一,记录当前位的下标,遍历结束后,从记录的下标开始往后都要变成9。

局部最优:遇到strNum[i - 1] > strNum[i]的情况,让strNum[i - 1]--,然后strNum[i]给为9,可以保证这两位变成最大单调递增整数

全局最优:得到小于等于N的最大单调递增的整数

class Solution {
    public int monotoneIncreasingDigits(int n) {
        // 转数组
        int[] arr = getArr(n);
        int start = arr.length;
        for (int i = arr.length - 1; i > 0; i--) {
            if (arr[i] < arr[i - 1]) {
                arr[i - 1] -= 1;
                start = i;
            }
        }
        for (int i = start; i < arr.length; i++) {
            arr[i] = 9;
        }
        // 转int类型
        return getInt(arr);
    }
    public int[] getArr(int n) {
        int len = 0;
        int temp = n;
        while (temp != 0) {
            temp /= 10;
            len++;
        } 
        int[] arr = new int[len];
        for (int i = len - 1; i >= 0; i--) {
            arr[i] = n % 10;
            n /= 10;
        }
        return arr;
    }
    public int getInt(int[] arr) {
        int res = 0;
        for (int i = 0; i < arr.length; i++) {
            res += arr[i];
            if (i != arr.length - 1) {
                res *= 10;
            }
        }
        return res;
    }
}

这里也可以使用字符数组来进行操作

class Solution {
    public int monotoneIncreasingDigits(int n) {
        String s = String.valueOf(n);
        char[] chars = s.toCharArray();
        int start = s.length();
        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));
    }
}

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

题目链接:714. 买卖股票的最佳时机含手续费

思路:我们只需要确定买入日期和卖出日期即可,遍历数组,当遇到比当前最小值还要小的价格就更新买入日期,当遇到可以赚钱的时候就将其利润加入到结果中,并更新买入的最小值。

class Solution {
    public int maxProfit(int[] prices, int fee) {
        int res = 0;
        int min = prices[0];
        for (int i = 1; i < prices.length; i++) {
            // 如果比最小值还小,更新买入的日期
            if (prices[i] < min) {
                min = prices[i];
            }
            // 如果能够赚到钱,更新总利润,更新最小值
            if (prices[i] - min - fee > 0) {
                res += prices[i] - min - fee;
                min = prices[i] - fee; // 这里更新最小值要减去手续费,因为并不确定是否真正卖出。(后面可能还有更合适的,防止不必要的手续费)
            }
            // 如果买入卖出都不合适,什么也不做
        }
        return res;
    }
}

968.监控二叉树

题目链接:968. 监控二叉树

思路:我们可以找到规律,叶子节点是一定不能存放摄像头的,因为一个摄像头可以监测三层的节点,如果放在叶子节点上就少了一层,由此我们可以确定遍历顺序位自底向上的后序遍历。我们可以设0为当前节点未被监测到,1为当前节点为监控节点,2为当前节点已经被监测到。

class Solution {
    private int count = 0;
    public int minCameraCover(TreeNode root) { // 后序遍历
        lastorder(root);
        // 如果根节点没有被覆盖到,要加一个
        if ((root.left == null || root.left.val == 2) && (root.right == null || root.right.val == 2)) count++;
        return count;
    }
    public void lastorder(TreeNode node) {
        if (node == null) return;
        lastorder(node.left);
        lastorder(node.right);
        // 叶子节点直接返回
        if (node.left == null && node.right == null) return;
        // 如果有一个孩子节点有监控,设当前节点位2
        if ((node.left != null && node.left.val == 1) || (node.right != null && node.right.val == 1)) {
            node.val = 2;
        }
        // 如果有一个孩子没有被覆盖到,设当前节点为1,监控数量加一
        if ((node.left != null && node.left.val == 0) || (node.right != null && node.right.val == 0)) {
            node.val = 1;
            count++;
        }
        // 当前节点的孩子都被覆盖到了,不用做操作。
    }
}

随想录中解法,代码更简洁

class Solution {
    int  res=0;
    public int minCameraCover(TreeNode root) {
        // 对根节点的状态做检验,防止根节点是无覆盖状态 .
        if(minCame(root)==0){
            res++;
        }
        return res;
    }
    /**
     节点的状态值:
       0 表示无覆盖 
       1 表示 有摄像头
       2 表示有覆盖 
    后序遍历,根据左右节点的情况,来判读 自己的状态
     */
    public int minCame(TreeNode root){
        if(root==null){
            // 空节点默认为 有覆盖状态,避免在叶子节点上放摄像头 
            return 2;
        }
        int left=minCame(root.left);
        int  right=minCame(root.right);
        
        // 如果左右节点都覆盖了的话, 那么本节点的状态就应该是无覆盖,没有摄像头
        if(left==2&&right==2){
            //(2,2) 
            return 0;
        }else if(left==0||right==0){
            // 左右节点都是无覆盖状态,那 根节点此时应该放一个摄像头
            // (0,0) (0,1) (0,2) (1,0) (2,0) 
            // 状态值为 1 摄像头数 ++;
            res++;
            return 1;
        }else{
            // 左右节点的 状态为 (1,1) (1,2) (2,1) 也就是左右节点至少存在 1个摄像头,
            // 那么本节点就是处于被覆盖状态 
            return 2;
        }
    }
}