代码随想录算法营||贪心算法

基础知识

1. 什么是贪心算法:通过找到局部最优找到全局最优

题目一:分发饼干

1. 题目:代码随想录 (programmercarl.com)

2. 思路:尽量用大饼干喂胃口大的小孩

  • 先将两个数组进行排序 ,从大到小遍历数组小孩和饼干,小孩用for循环外层遍历,饼干用while循环内层遍历
  • 为什么不能外层是饼干:
    • 外面的 for 是里的下标 i 是固定移动的,而 if 里面的下标 index 是符合条件才移动的。
    • 如果 for 控制的是饼干, if 控制胃口,就是出现如下情况 :

class Solution {
    public int findContentChildren(int[] g, int[] s) {
        Arrays.sort(g);
        Arrays.sort(s);
        int index=s.length-1;
        int ans=0;
        for(int i=g.length-1;i>=0;i--){//遍历胃口
            if(index>=0 && s[index]>=g[i]){
                index--;
                ans++;
            }
        }
        return ans;
    }
}

题目二:摆动序列

1. 题目:376. 摆动序列 - 力扣(LeetCode) 

2. 思路:局部最优是单调坡里的元素都删除掉,全局最优是得到摆动最长的序列

(1)因为输出的是长度,所以不需要输出摆动子序列,遇到摆动就++

(2)判断摆动:prediff=nums[i]-nums[i]-1;curdiff=nums[i+1]-nums[i]是一正一负

(3)上下坡有平坡:例如1,2,2,2,1的最大摆动序列长度是3

class Solution {
    public int wiggleMaxLength(int[] nums) {
        if (nums.length <= 1)return nums.length;
        //当前差值
        int curDiff = 0;
        //上一个差值
        int preDiff = 0;
        int count = 1;
        for (int i = 1; i < nums.length; i++) {
            //得到当前差值
            curDiff = nums[i] - nums[i - 1];
            //如果当前差值和上一个差值为一正一负
            //等于0的情况表示初始时的preDiff
            if ((curDiff > 0 && preDiff <= 0) || (curDiff < 0 && preDiff >= 0)) {
                count++;
                preDiff = curDiff;
            }
        }
        return count;
    }
}

题目三:最大子序和

1. 题目:53. 最大子数组和 - 力扣(LeetCode) 

2. 思路:局部最优是如果和为负数则删掉,全局最优是找到最大连续子数组的和

(1) 先将result更新,再判断if(sum<0)sum=0

class Solution {
    public int maxSubArray(int[] nums) {
        if(nums.length==1)return nums[0];
        int sum=0;
        int result=Integer.MIN_VALUE;
        for (int i=0;i<nums.length;i++){
            sum+=nums[i];
            if (sum<=0)sum=0;
            else result=Math.max(result,sum);
        }
        return result;
    }
}

题目四:买卖股票的最佳时机||

1. 题目:122. 买卖股票的最佳时机 II - 力扣(LeetCode) 

2. 思路:

(1)局部最优是遇到利润为正数就加起来,全局最优是最后收集到了最大利润

   public int maxProfit(int[] prices) {
        int ans=0;
        for (int i=1;i<prices.length;i++){
            if (prices[i]-prices[i-1]>0)ans+=prices[i]-prices[i-1];
        }
        return ans;
    }

题目五:跳跃游戏

1. 题目:55. 跳跃游戏 - 力扣(LeetCode) 

2. 思路:看能不能覆盖到最后的位置,for循环是遍历已覆盖的范围,不是全部的数组

 public boolean canJump(int[] nums) {
           int cover=0;
        for (int i=0;i<=cover;i++){
            cover=Math.max(i+nums[i],cover);
            if (cover>=nums.length-1)return true;
        }
        return false;
    }

题目六:跳跃游戏

1. 题目:45. 跳跃游戏 II - 力扣(LeetCode) 

2. 思路:每次都找到,下一步的覆盖范围

public int jump(int[] nums) {
        int result = 0;
        // 当前覆盖的最远距离下标
        int end = 0;
        // 下一步覆盖的最远距离下标
        int temp = 0;
        for (int i = 0; i <= end && end < nums.length - 1; ++i) {
            temp = Math.max(temp, i + nums[i]);
            // 可达位置的改变次数就是跳跃次数
            if (i == end) {
                end = temp;
                result++;
            }
        }
        return result;
    }

题目七:K次取反后最大化的数组和

1. 题目:1005. K 次取反后最大化的数组和 - 力扣(LeetCode) 

2. 思路

(1)第一次贪心:将所有的负数转化为整数,优先绝对值大的数

(2)第二次贪心:都转化为正数之后,选取最小的正数取反直到消耗完k

(3)按照绝对值的大小进行排序

  • 是负数:if(nums[i<0 && k<0])nums*=-1;k--
  • 都取完之后:如果k是奇数则对最小的数取反一次,如果是偶数不用进行操作
public int largestSumAfterKNegations(int[] nums, int k) {
        Arrays.sort(nums);//1.排序数组,将数组中负数转化为正数
        for (int i=0;i<nums.length && k>0;i++){
            if (nums[i]<0){
                nums[i]=-nums[i];
                k--;
            }
        }
        //2.剩余k如果是奇数则重新排序,取最小的取反
        if (k%2!=0){
            Arrays.sort(nums);
            nums[0]*=-1;
        }
        //3.数组中所有元素相加,得到ans
        int ans=0;
        for (int i:nums){
            ans+=i;
        }
        return ans;
    }

题目八:加油站

1. 题目:134. 加油站 - 力扣(LeetCode)

2. 思路:(1)先用gas-cost计算剩余的油量,在一个for循环里计算totalsum和cursum;如果cursum<0则遍历下一个站点;如果totalsum<0则一定没有答案

  int cursum=0,totalsum=0,ans=0;
        for (int i=0;i<gas.length;i++){
            cursum+=gas[i]-cost[i];
            totalsum+=gas[i]-cost[i];
            if (cursum<0){
                ans=i+1;
                cursum=0;
            }
        }
        if (totalsum<0)return -1;
        return ans;

题目九:分发糖果

1. 题目:135. 分发糖果 - 力扣(LeetCode) :每个孩子至少分配到 1 个糖果;相邻的孩子中,评分高的孩子必须获得更多的糖果。

2. 思路:只比较一边,不比较两边

(1)如果是[1,2,2]则分发[1,2,1]个糖果,因为第二个2没有比第一个二高,所以就发一个

(2)右孩比左孩评分高,从前向后遍历

(3)左孩比右孩评分高,从后向前遍历

class Solution {
    public int candy(int[] ratings) {
        //1、起点下标1 从左往右,只要 右边 比 左边 大,右边的糖果=左边 + 1
        int[] candy1=new int[ratings.length];
        candy1[0]=1;
        int res=0;
        for (int i=1;i<candy1.length;i++){
            if (ratings[i]>ratings[i-1])candy1[i]=candy1[i-1]+1;
            else candy1[i]=1;
        }
        //2、起点下标 ratings.length - 2 从右往左, 只要左边 比 右边 大,
        // 左边的糖果应该 取本身的糖果数(符合比它左边大) 和 右边糖果数 + 1 二者的最大值
        for (int i=candy1.length-2;i>=0;i--){
            if (ratings[i]>ratings[i+1]){
                candy1[i]=Math.max(candy1[i],candy1[i+1]+1);
            }
            
        }
        //3.计算和
        for(int i:candy1){
            res+=i;
        }
       return res;
}
}

题目十:柠檬水找零

1. 题目:860. 柠檬水找零 - 力扣(LeetCode)

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

题目十一:根据身高排队列

1. 题目:406. 根据身高重建队列 - 力扣(LeetCode) 

2. 思路:有两个排序条件的时候一个一个来

(1)先按照k进行排序,再按照身高进行排序不太行

(2)采用先按照身高从大到小排序,排序的时候根据k进行调整:用排序方法的时候升序是a-b,降序是b-a

   public int[][] reconstructQueue(int[][] people) {
        //1.先将people进行排序
        Arrays.sort(people,(a,b) ->{//people数组中的a,b
            if (a[0]==b[0])return a[1]-b[1];//如果身高相等,则升序看k值
            return b[0]-a[0];//如果身高不等,降序看身高
                }
        );
        LinkedList<int[]> que=new LinkedList<>();//linkedlist方便增加删除
        for (int i=0;i<people.length;i++){
            que.add(people[i][1],people[i]);//按照他的k进行排序
        }
        return que.toArray(new int[people.length][]);
    }

题目十二:用最少数量的箭引爆气球

1. 题目:452. 用最少数量的箭引爆气球 - 力扣(LeetCode)

2. 思路:只需要记录几只箭不需要把气球从数组中移除

(1)按照左边界进行排序;比较的是当前气球和前一个气球,所以遍历从1开始

(2)添加弓箭:如果第i个的左边界大于前一个的右边界 if(point[i][0]>point[i-1][0]),因为两个气球连在一起也可以射

(3)如果两个气球重叠了,就会更新第i个气球的右边界Math.min(第i-1个气球的右边界,第i个气球的右边界)

public int findMinArrowShots(int[][] points) {
           Arrays.sort(points,(a,b)->Integer.compare(a[0],b[0]));
        int ans=1;
        for (int i=1;i<points.length;i++){
            //1.比较i的左边界和i-1的右边界
            if (points[i][0]>points[i-1][1])ans++;
            else {//2.比较右边界
                points[i][1]=Math.min(points[i-1][1],points[i][1]);
            }
        }
        return ans;
    }

题目十三:无重叠区间

1. 题目:. - 力扣(LeetCode) 

2. 思路:判断有几个重叠区间删除之后就是无重叠区间了;左边界排序

(1)如果第i个的左边界>第i-1个右边界,则不重叠;若重叠,则取i和i-1右边界的最小值,在和下一个进行比较

 public int eraseOverlapIntervals(int[][] intervals) {
        //1.对数组进行排序
        Arrays.sort(intervals,(a,b)->Integer.compare(a[0],b[0])); 
        int ans=0;
        for(int i=1;i<intervals.length;i++){//从1开始找重叠区间
            if(intervals[i][0]<intervals[i-1][1]){
                ans++;
                intervals[i][1]=Math.min(intervals[i-1][1],intervals[i][1]);
            }
        }
        return ans;
    }

题目十四:划分字母区间

1. 题目:763. 划分字母区间 - 力扣(LeetCode)

2. 先遍历一遍字符串,找到每个字母最远的位置,保存到数组中,然后遍历字符串

 public List<Integer> partitionLabels(String s) {
         List<Integer> ans=new ArrayList<>();
        int[] farest=new int[26];
        char[] chars=s.toCharArray();
        for (int i=0;i<s.length();i++){
            farest[chars[i]-'a']=i;
        }
        int left=0,right=0;
        for (int i=0;i<s.length();i++){
            right=Math.max(right,farest[chars[i]-'a']);
            if (i==right){
                ans.add(right-left+1);
                left=i+1;
            }
        }
        return ans;
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值