day37【代码随想录】贪心算法之划分字母区间、合并区间、单调递增的数字、买卖股票的最佳时机含手续费、监控二叉树


前言

1、划分字母区间
2、合并区间
3、单调递增的数字
4、买卖股票的最佳时机含手续费
5、监控二叉树


一、划分字母区间(力扣763)

给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。

注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s 。

返回一个表示每个字符串片段的长度的列表。
在这里插入图片描述
思路:
在遍历的过程中相当于是要找每一个字母的边界,如果找到之前遍历过的所有字母的最远边界,说明这个边界就是分割点了。此时前面出现过所有字母,最远也就到这个边界了。

  • 统计每一个字符最后出现的位置
  • 从头遍历字符,并更新字符的最远出现下标,如果找到字符最远出现位置下标和当前下标相等了,则找到了分割点
    在这里插入图片描述
class Solution {
    public List<Integer> partitionLabels(String s) {
        List<Integer> res = new LinkedList<>();
        char[] chars = s.toCharArray();
        int[] edge = new int[26];
        for(int i=0;i<chars.length;i++){
            edge[chars[i]-'a']=i;
        }

        int idx = 0;
        int last = -1;
        for(int i=0;i<chars.length;i++){
            idx = Math.max(idx,edge[chars[i]-'a']);
            if(i==idx){
                res.add(i-last);
                last=i;
            }
        }
        return res;
    }
}

在这里插入图片描述

二、合并区间(力扣56)

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

在这里插入图片描述
思路:
在这里插入图片描述

class Solution {
    public int[][] merge(int[][] intervals) {

        List<int[]> res = new LinkedList<>();
        Arrays.sort(intervals,(a,b)->Integer.compare(a[0],b[0]));
        int start = intervals[0][0];
        int rightBound = intervals[0][1];
        for(int i=1;i<intervals.length;i++){
            if(intervals[i][0]>rightBound){ //不挨着
               res.add(new int[]{start,rightBound});
               start = intervals[i][0];
               rightBound = intervals[i][1];
            }
            else{//挨着,更新右边界
                rightBound = Math.max(rightBound,intervals[i][1]);
            }
        }
        res.add(new int[]{start,rightBound});
        return res.toArray(new int[res.size()][]);
    }
}

在这里插入图片描述

三、单调递增的数字(力扣738)

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

给定一个整数 n ,返回 小于或等于 n 的最大数字,且数字呈 单调递增 。
在这里插入图片描述
思路:
例如:98,一旦出现strNum[i - 1] > strNum[i]的情况(非单调递增),首先想让strNum[i - 1]–,然后strNum[i]给为9,这样这个整数就是89,即小于98的最大的单调递增整数

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

是从前向后遍历还是从后向前遍历呢?

举个例子,数字:332,从前向后遍历的话,那么就把变成了329,此时2又小于了第一位的3了,真正的结果应该是299。
所以从前向后遍历会改变已经遍历过的结果!
那么从后向前遍历,就可以重复利用上次比较得出的结果了,从后向前遍历332的数值变化为:332 -> 329 -> 299

class Solution {
    public int monotoneIncreasingDigits(int N) {
        String s = String.valueOf(N);
        char[] chars = s.toCharArray();

        int start = chars.length;
        for(int i=start-2;i>=0;i--){
            if(chars[i]>chars[i+1]){
                chars[i]--;
                start = i+1;
            }
        }
        for(int i = start;i<chars.length;i++){
            chars[i]='9';
        }
        return Integer.parseInt(String.valueOf(chars));
    }
}

在这里插入图片描述

四、买卖股票的最佳时机含手续费(力扣714)

给定一个整数数组 prices,其中 prices[i]表示第 i 天的股票价格 ;整数 fee 代表了交易股票的手续费用。

你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。

返回获得利润的最大值。

注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。
思路:

  • 用两个变量,一个累加利润用,另一个保存当前最优成本
  • 当前成本即 -> 买入价格 + 手续费
  • 如果哪天价格比这个成本要高,就可以考虑在这天卖了,这时更新成本为当前的价格
  • 如果日后哪天价格更优,可以晚一会儿再卖嘛。
  • 当然,如果哪天的 价格 + 成本,要比手里的成本还低,那就在这天去买就好了。
  • 综上,这天卖不卖,不确定,能有利润我就先记上,以后要是有更高的,我把又涨了的利润也叠加进去,要是有更低的,我就当之前已经卖了,再买新的。只是模拟出利润最大化。
class Solution {
    public int maxProfit(int[] prices, int fee) {
        int buy = fee + prices[0];//成本即 -> 买入价格 + 手续费
        int res = 0;
        for(int i = 1;i<prices.length;i++){
            if(prices[i]+fee<buy){ //选一个买入价钱更小的
                buy = prices[i]+fee; //更新值
            }else if(prices[i]>buy){ //哪天价格比这个成本要高,就可以考虑在这天卖了
                res += prices[i]-buy; //利润相加
                buy = prices[i]; //更新成本为当前的价格
            }
        }
        return res;
    }
}

在这里插入图片描述

五、监控二叉树(力扣968)

给定一个二叉树,我们在树的节点上安装摄像头。

节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。

计算监控树的所有节点所需的最小摄像头数量。

在这里插入图片描述
在这里插入图片描述
思路:
确定遍历顺序:后序遍历(从低向上推导)
如何隔两个节点放一个摄像头

每个节点可能有三种状态:
该节点无覆盖:0
本节点有摄像头:1
本节点有覆盖:2

单层逻辑处理:
情况1:左右节点都有覆盖,那么此时中间节点应该就是无覆盖的状态了。
情况2:左右节点至少有一个无覆盖的情况,则中间节点(父节点)应该放摄像头
情况3:左右节点至少有一个有摄像头,其父节点就应该是2(覆盖的状态)
情况4:头结点没有覆盖

/**
 * 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;
    public int minCameraCover(TreeNode root) {
        if(minCame(root)==0){ //情况四
            res++;
        }
        return res;
    }
    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) 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、付费专栏及课程。

余额充值