leetcode贪心算法总结

文章目录

  • 一、入门
    • T455. 分发饼干
  • 二、中等
    • 1. 序列问题
      • 376. 摆动序列
    • 2. 两个维度权衡问题
      • T406. 根据身高重建队列
    • 3. 股票问题
  • 三、进阶
    • T53. 最大子数组和(dp或者贪心)
    • T134. 加油站
    • T968. 监控二叉树
    • 0. 区间问题
      • T55. 跳跃游戏 (能否到达)
      • T45. 跳跃游戏Ⅱ ****
      • T452. 用最少数量的箭引爆气球
      • T435. 无重叠区间
      • T763. 划分字母区间
      • T56. 合并区间

leetcode贪心算法总结_第1张图片

一、入门

T455. 分发饼干

leetcode贪心算法总结_第2张图片

//小饼干给小胃口
class Solution {
    public int findContentChildren(int[] g, int[] s) {
        Arrays.sort(g);
        Arrays.sort(s);
        int count = 0;
        int start = 0;
        for(int i = 0;i<s.length&&start<g.length;i++){
            if(s[i]>=g[start]){
                start++;
                count++;
            }
        }
        return count;
    }
}

二、中等

1. 序列问题

376. 摆动序列

leetcode贪心算法总结_第3张图片

class Solution {
    //删除单调坡上的节点
    public int wiggleMaxLength(int[] nums) {
        if (nums.length == 1){return 1;}
        //记录差值
        int pre = 0; 
        int cur = 0;
        int count = 1;
        for(int i = 1;i<nums.length;i++){
            cur = nums[i] - nums[i-1];
            if ((cur>0&&pre<=0)|| (cur<0&&pre>=0)){//pre需要取等号
                count++;
                pre = cur;
            }
        }
        return count;
    }
}

2. 两个维度权衡问题

T406. 根据身高重建队列

leetcode贪心算法总结_第4张图片

class Solution {
    //优先按身高高的people的k来插入 插入操作后的people满足队列属性
    //二维分开处理
    //先对身高排序 高的在前 若相同就看k k小的在前面
    //再根据k插入
    public int[][] reconstructQueue(int[][] people) {
        //lambda表达式实现对二维数组的某一维排序

        Arrays.sort(people,(x,y)->{
            if(x[0] == y[0]){return x[1] - y[1];}
            return y[0] - x[0];
        });

        LinkedList<int[]> que = new LinkedList<>();
        for(int[] p:people){
            que.add(p[1],p);
        }

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

    }
}

3. 股票问题

三、进阶

T53. 最大子数组和(dp或者贪心)

leetcode贪心算法总结_第5张图片

class Solution {
    //局部最优:出现负数就重新开始
    public int maxSubArray(int[] nums) {
        if(nums.length == 1){return nums[0];}
        int sum = Integer.MIN_VALUE;
        int count = 0;
        for(int i = 0;i<nums.length;i++){
            count = count + nums[i];
            sum = Math.max(count,sum);
            if(count<0){
                count=0;//出现负数 重新开始取数
            }
        }
        return sum;
    }
}

T134. 加油站

leetcode贪心算法总结_第6张图片

  • 思路分析
    总的耗油量必须大于0;
    如果出现求和小于0,那么必然不是起点
class Solution {
//局部最优:当前累加rest[j]的和curSum一旦小于0,起始位置至少要是j+1,因为从j开始一定不行
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int rest = 0;
        int sum = 0;
        int index = 0;
        for(int i = 0;i<gas.length;i++){
            rest += gas[i] - cost[i];
            sum += gas[i] - cost[i];//加的油和消耗的油之差
            if(rest<0){
                rest = 0;
                index = i + 1;
            }
        }
        if(sum<0){return -1;}//此时必然不可能跑一周
        return index;
    }
}

T968. 监控二叉树

leetcode贪心算法总结_第7张图片

  • 思路分析
    从下往上看,局部最优:让叶子节点的父节点安摄像头,所用摄像头最少,整体最优:全部摄像头数量所用最少!
    每个节点可能有3种状态:
    该节点无覆盖
    本节点有摄像头
    本节点有覆盖
  • 代码实现
 //局部最优:让叶子节点的父节点放摄像头
 //后序遍历:左右中
 //传递进去的root节点需要由左右节点的状态来决定如何处理
 //状态0:安装了监控;1:可以观察到,但没监控;2:观察不到
class Solution {
    int sum = 0;
    public int minCameraCover(TreeNode root) {
        if(root == null){return 0;}
        if(minHelper(root) == 2){sum++;}//如果根节点没装监控 需要加1
        return sum;
    }
    public int minHelper(TreeNode root){
        //终止条件
        if(root == null){
            return 1;
        }
        //计算左右节点的状态
        int left = minHelper(root.left);
        int right = minHelper(root.right);
        //分情况处理root
        if (left == 2 || right == 2){ //两个左右孩子有一个观察不到
            sum++;
            return 0;
        }else if(left == 1 && right == 1){//左右孩子观察得到 但是没装
            return 2;
        }else{
            return 1;
        }
    }
}

0. 区间问题

T55. 跳跃游戏 (能否到达)

leetcode贪心算法总结_第8张图片

class Solution {
    //不管跳多少步 只要能覆盖到最后一层就好
    public boolean canJump(int[] nums) {
        if(nums.length == 1){return true;}
        int cover = 0;
        for(int i = 0;i<=cover;i++){ //当i超出cover范围后就无法跳跃了
            cover = Math.max(cover,i+nums[i]);
            if(cover>=nums.length-1){
                return true;
            }
        }
        return false;
    }
}

T45. 跳跃游戏Ⅱ ****

leetcode贪心算法总结_第9张图片

  • 思路分析
    如果某一个作为 起跳点 的格子可以跳跃的距离是 3,那么表示后面 3 个格子都可以作为 起跳点。
  • 可以对每一个能作为 起跳点 的格子都尝试跳一次,把 能跳到最远的距离 不断更新。
    如果从这个 起跳点 起跳叫做第 1 次 跳跃,那么从后面 3 个格子起跳 都 可以叫做第 2 次 跳跃。
    所以,当一次 跳跃 结束时,从下一个格子开始,到现在 能跳到最远的距离,都 是下一次 跳跃 的 起跳点。
  • 对每一次 跳跃 用 for 循环来模拟。
  • 跳完一次之后,更新下一次 起跳点 的范围。
  • 在新的范围内跳,更新 能跳到最远的距离。
    记录 跳跃 次数,如果跳到了终点,就得到了结果。
    leetcode贪心算法总结_第10张图片
  • 代码实现
class Solution {
    public int jump(int[] nums) {
        if(nums.length == 1) return 0;
        int start = 0;//每一次跳跃的起点
        int cover = nums[0];//最大覆盖范围,也是每次跳跃的终点
        int step = 1;//记录步数,
        while(cover < nums.length-1){//覆盖没有到达终点,一次while循环是一次跳跃
            int tempStart = start;
            int tempEnd = cover;
            for(int i=tempStart;i<=tempEnd;i++){
                if(i+nums[i]>cover){
                    cover = i+nums[i];
                    start = i + 1;
                }
            }
            step++;
        }
        return step;
    }
}

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

leetcode贪心算法总结_第11张图片

class Solution {
    //局部最优:重叠在一起
    //左边从小到大排序,更新右边界
    public int findMinArrowShots(int[][] points) {
        int count = 1;
        Arrays.sort(points,(a,b)->{
            return Integer.compare(a[0]-b[0]);
        });
        for(int i=1;i<points.length;i++){
            if(points[i][0]>points[i-1][1]){
                count++;//无重叠计数加1
            }
            else{
                points[i][1] = Math.min(points[i][1],points[i-1][1]);//更新右边界
            }
        }
        return count;
    }
}

T435. 无重叠区间

leetcode贪心算法总结_第12张图片
思路分析:
排序(左边界从小到大),出现重叠就更新右边界

class Solution {
    public int eraseOverlapIntervals(int[][] intervals) {
        int count=0;//记录重复的区间
        //排序:按第一个元素 升序排列
        Arrays.sort(intervals,(x,y)->{
            return x[0]-y[0];
        });
        for(int i=1;i<intervals.length;i++){
            if(intervals[i-1][1]>intervals[i][0]){//前一个的右边界大于当前的左边界.出现重复了
                count++;
                intervals[i][1] = Math.min(intervals[i-1][1],intervals[i][1]);//更新右边界
            }

        }
        return count;
    }
}

T763. 划分字母区间

leetcode贪心算法总结_第13张图片

class Solution {
    //找最大值切割
    //画图分析
    public List<Integer> partitionLabels(String s) {
        int[] sList = new int[26];
        char[] ch = s.toCharArray();
        List<Integer> res = new ArrayList<>();
        for(int i = 0;i<ch.length;i++){
            sList[ch[i]-'a'] = i;
        }
        int last = -1;//记录最后一个索引,求长度可用
        int maxIdx = 0;//记录最大边界
        for(int i = 0;i<ch.length;i++){
            maxIdx = Math.max(maxIdx,sList[ch[i]-'a']);//记录最大值
            if (i == maxIdx){ 
                res.add(i-last);
                last=i;
            }
        }
        return res;
    }   
}

T56. 合并区间

leetcode贪心算法总结_第14张图片

class Solution {
    //i-1的右边界大于i的左边界就进行合并
    public int[][] merge(int[][] intervals) {
        Arrays.sort(intervals,(a,b)->Integer.compare(a[0],b[0]));
        LinkedList<int[]> res = new LinkedList<>();//选择linkedLIST
        res.add(intervals[0]);//存放当前区间,遍历的时候进行比较
        for(int i = 1;i<intervals.length;i++){
            if(intervals[i][0]<=res.getLast()[1]){//右边界小于等于左边界
                int left = res.getLast()[0];
                int right = Math.max(intervals[i][1],res.getLast()[1]);
                res.removeLast();
                res.add(new int[]{left,right});//更新左右边界
            }else{
                res.add(intervals[i]);//直接加入结果
            }
        }
        return res.toArray(new int[res.size()][]);//linkedlist变成二维array
    }
}

你可能感兴趣的:(算法题,leetcode,贪心算法,算法)