代码随想录打卡-贪心算法

一、贪心简单题

1.1 分发饼干

455.分发饼干

class Solution {
    public int findContentChildren(int[] g, int[] s) {
        //排序
        Arrays.sort(g);
        Arrays.sort(s);
        
        int i = 0,j = 0;
        while(i<s.length && j<g.length){
            if(s[i]>=g[j]){
                i++;
                j++;
            }else{
                i++;
            }
        }
        return j;
    }
}

1.2 K次取反后最大化的数组和

1005.K次取反后最大化的数组和

class Solution {
    public int largestSumAfterKNegations(int[] nums, int k) {
        //有负数先取反
        Arrays.sort(nums);
        for(int i = 0;i<nums.length;i++){
            if(nums[i]<0 && k>0){
                nums[i] = -nums[i];
                k--;
            }else{
                break;
            }
        }
        //k还大于0,并且为奇数
        if(k>0 && k%2!=0){
            //重新排序
            Arrays.sort(nums);
            nums[0] = -nums[0];
        }
        //遍历求和
        int sum = 0;
        for(int i = 0;i<nums.length;i++){
            sum += nums[i];
        }
        return sum;
    }
}

1.3 柠檬水找零

860.柠檬水找零

注意20找15的时候,应优先找一张5一张10,有助于保留更多的5,用于后续交易

class Solution {
    public boolean lemonadeChange(int[] bills) {
        int fiveCount = 0;
        int tenCount = 0;
        for(int i = 0;i<bills.length;i++){
            if(bills[i]==5){
                fiveCount++;
            }else if(bills[i]==10){
                if(fiveCount<1){
                    return false;
                }
                fiveCount--;
                tenCount++;
            }else if(bills[i]==20){
                if(fiveCount>0 && tenCount>0){
                    fiveCount--;
                    tenCount--;
                }else if(fiveCount>=3){
                    fiveCount -= 3;
                }else{
                    return false;
                }
            }
        }
        return true;
    }
}

二、贪心中等题

2.1 摆动序列

376.摆动序列

class Solution {
    public int wiggleMaxLength(int[] nums) {
        if(nums.length<=1){
            return nums.length;
        }
        int res = 1;
        int curDiff = 0;
        int preDiff = 0;
        for(int i = 1;i<nums.length;i++){
            curDiff = nums[i]-nums[i-1];
            //preDiff等于0时,是初始状态
            //若没有进入if中,res未改变,preDiff也没有被赋新值
            //本题只要求返回子序列长度,故可以不删除数组元素
            if(curDiff>0 && preDiff<=0 || curDiff<0 && preDiff>=0){
                res++;
                preDiff = curDiff;
            }
        }
        return res;
    }
}

2.2 单调递增的数字

738.单调递增的数字

注意点:整数如何转换为字符串数组 --- 利用+"" 以及 split("")

                字符串如何转换为整数 --- Integer.parseInt()

                字符串数组如何连接成一个字符串 --- String.join("",str)

class Solution {
    public int monotoneIncreasingDigits(int n) {
        //+""的作用,将前面的表达式转换成字符串类型
        //split("拆分字符串的符号")
        String[] str = (n+"").split("");
        int index = str.length;
        for(int i = str.length-1;i>0;i--){
            //Integer.parseInt():字符串转换为整数
            if(Integer.parseInt(str[i])<Integer.parseInt(str[i-1])){
                str[i-1] = (Integer.parseInt(str[i-1])-1)+"";
                index = i;
            }
        }
        for(int i = index;i<str.length;i++){
            str[i] = "9";
        }

        //String.join("用来连接的符号",需连接起来的字符)
        return Integer.parseInt(String.join("",str));
    }
}

2.3 买卖股票的最佳时机 II

122.买卖股票的最佳时期II

class Solution {
    //重点:把整理利润拆分成每天的利润
    //贪心:只收集每天的正利润
    public int maxProfit(int[] prices) {
        int res = 0;
        for(int i = 1;i<prices.length;i++){
            if(prices[i]-prices[i-1]>0){
                res += prices[i]-prices[i-1];
            }
        }
        return res;
    }
}

2.4 两个维度权衡问题

重点:不要顾此失彼,先确定一个维度,再考虑另一个维度

2.4.1 分发糖果

135.分发糖果

class Solution {
    public int candy(int[] ratings) {
        //先从左到右
        int sum = 0;
        int[] surger = new int[ratings.length];
        surger[0] = 1;
        for(int i = 1;i<ratings.length;i++){
            if(ratings[i]>ratings[i-1]){
                surger[i] = surger[i-1]+1;
            }else{
                surger[i] = 1;
            }
        }
        //从右到左
        for(int i = ratings.length-1;i>0;i--){
            if(ratings[i-1]>ratings[i] && surger[i-1]<=surger[i]){
                surger[i-1] = surger[i]+1;
            }
        }

        //遍历
        for(int i = 0;i<surger.length;i++){
            sum += surger[i];
        }

        return sum;
    }
}
2.4.2 根据身高重建队列

406.根据身高重建队列

关键步骤:res.add(p[1],p);

class Solution {
    public int[][] reconstructQueue(int[][] people) {
        //身高从大到小排,遇到相同身高的,k值小的在前面
        //a-b升序   b-a降序
        Arrays.sort(people,(a,b)->{
            if(a[0]==b[0]){
                return a[1]-b[1];
            }else{
                return b[0]-a[0];
            }
        });
        //便于动态插入元素
        ArrayList<int[]> res = new ArrayList<>();
        //LinkedList<int[]> res = new LinkedList<>();
        for(int[] p:people){
            res.add(p[1],p);
        }

        return res.toArray(new int[people.length][2]);
    }
}

三、贪心难题

3.1 区间问题

3.1.1 跳跃游戏

55.跳跃游戏

注意:在目前覆盖范围中更新!for循环的边界要明确,不是整个数组的边界,而应该是目前覆盖范围的边界!

class Solution {
    public boolean canJump(int[] nums) {
        //在目前覆盖范围中更新最大覆盖范围
        int coverRange = 0;
        for(int i = 0;i<=coverRange;i++){
            coverRange = Math.max(coverRange,i+nums[i]);
            if(coverRange>=nums.length-1){
                return true;
            }
        }
        return false;
    }
}
3.1.2 跳跃游戏II

45.跳跃游戏II

class Solution {
    //一步尽可能多走,到不了就多走一步
    public int jump(int[] nums) {
        int curCoverRange = 0;
        int maxCoverRange = 0;
        int ans = 0;
        if(nums.length<=1 || nums==null ){
            return 0;
        }
        for(int i = 0;i<nums.length;i++){
            //更新最大范围
            maxCoverRange = Math.max(maxCoverRange,i+nums[i]);
            if(maxCoverRange>=nums.length-1){
                ans++;
                break;
            }
            //走到了当前范围的最后,更新下一步可达的最大范围
            if(i==curCoverRange){
                ans++;
                curCoverRange = maxCoverRange;
            }
        }
        return ans;
    }
}
3.1.3 用最少数量的箭引爆气球

452.用最少数量的箭引爆气球

class Solution {
    public int findMinArrowShots(int[][] points) {
        Arrays.sort(points,(a,b)->Integer.compare(a[0],b[0]));
        int count = 1;
        for(int i = 1;i<points.length;i++){
            if(points[i][0]<=points[i-1][1]){
                //重叠了
                //更新重叠气球最小右边界
                points[i][1] = Math.min(points[i][1],points[i-1][1]);
            }else{
                count++;
            }
        }
        return count;
    }
}
3.1.4 无重叠区间

435.无重叠区间

class Solution {
    public int eraseOverlapIntervals(int[][] intervals) {
        Arrays.sort(intervals,(a,b)->Integer.compare(a[0],b[0]));
        int count = 0;
        
        for(int i = 1;i<intervals.length;i++){
            if(intervals[i-1][1]>intervals[i][0]){
                //有重叠
                intervals[i][1] = Math.min(intervals[i][1],intervals[i-1][1]);
                count++;
            }
        }
        return count;

    }
}
3.1.5 划分字母区间

763.划分字母区间

class Solution {
    public List<Integer> partitionLabels(String s) {
        char[] chars = s.toCharArray();
        int[] record = new int[26];
        //记录该字母最后出现的索引位置
        for(int i = 0;i<chars.length;i++){
            record[chars[i]-'a'] = i;
        }

        List<Integer> res = new ArrayList<>();
        int index = 0;
        int last = -1;
        for(int i = 0;i<chars.length;i++){
            index = Math.max(index,record[chars[i]-'a']);
            if(index==i){
                res.add(i-last);
                last = i;
            }
        }
        return res;
    }
}
3.1.6 合并区间

56.合并区间

注意:此时不是i和i-1进行比较了,因为这里返回的是int[][],需要更新上一个区间的记录,出现重叠,则上一个区间不再是i-1了!

class Solution {
    public int[][] merge(int[][] intervals) {
        Arrays.sort(intervals,(a,b)->Integer.compare(a[0],b[0]));
        LinkedList<int[]> res = new LinkedList<>();
        int start = intervals[0][0];
        int rightRange = intervals[0][1];
        for(int i = 1;i<intervals.length;i++){
            if(rightRange>=intervals[i][0]){
                //重叠了
                rightRange = Math.max(intervals[i][1],rightRange);
            }else{
                res.add(new int[]{start,rightRange});
                start = intervals[i][0];
                rightRange = intervals[i][1];
            }
        }
         res.add(new int[]{start,rightRange});
        return res.toArray(new int[res.size()][2]);
    }
}

3.2 其它难题

3.2.1 最大子数组和

53.最大子数组和

class Solution {
    public int maxSubArray(int[] nums) {
        //加起来为负数,对后面是减小的效果,舍去
        int sum = Integer.MIN_VALUE;
        int tmp = 0;
        for(int i = 0;i<nums.length;i++){
            tmp += nums[i];
            sum = Math.max(sum,tmp);
            if(tmp<0){
                tmp=0;
            }
        }
        return sum;
    }
}
3.2.2 加油站

134.加油站

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int curSum = 0;
        int totalSum = 0;
        int index = 0;
        for(int i = 0;i<gas.length;i++){
            curSum += gas[i]-cost[i];
            totalSum += gas[i]-cost[i];
            if(curSum<0){
                index = (i+1)%gas.length;
                curSum = 0;
            }
        }
        if(totalSum<0){
            return -1;
        }

        return index;
        
    }
}
3.2.3 监控二叉树

968.监控二叉树

从下往上;

局部最优:给叶子节点的父节点装摄像头

注意:

1、最后的节点若为空,则需要额外加个摄像头

2、设置三个状态:0无覆盖、1有摄像头、2有覆盖

3、存在三种情景:左右节点均有覆盖---说明该父节点无覆盖且无摄像头

                               左右节点至少一个无覆盖---说明该父节点应加一个摄像头

                               左右节点至少存在一个摄像头---说明该父节点被覆盖

4、遇到空节点应设置为什么状态:若为0,则需要在叶子节点加一个摄像头

                                                        若为1,则叶子节点被覆盖了,叶子节点的父节点就不需要装摄像头了

/**
 * 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(minCamera(root)==0){
            res++;
        }
        return res;
    }

    private int minCamera(TreeNode root){
        //0:未覆盖
        //1:有摄像头
        //2:已覆盖
        //后序遍历:左右中
        if(root==null){
            return 2;//空节点不能是0,这样叶子节点上就需要有摄像头;不能是1,这样叶子节点的父节点就没必要放摄像头了
        }
        int left = minCamera(root.left);
        int right = minCamera(root.right);

        //左右节点都被覆盖,则该节点上是无摄像头、无覆盖状态
        if(left==2 && right==2){
            return 0;
        }else if(left==0 || right==0){
            //至少有一个左右节点没被覆盖,则该节点需要加个摄像头
            res++;
            return 1;
        }else{
            //其他情况:左右节点至少存在一个摄像头,所以该节点被覆盖
            return 2;
        }
    }
}

  • 25
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值