常见的贪心算法题总结

本文介绍了贪心算法在多种IT技术问题中的应用,如分发饼干、摆动数列、最大子数组和等,强调了贪心法的核心思想——局部最优推导整体最优,并分享了解决具体问题时的技巧和实例。
摘要由CSDN通过智能技术生成

贪心

刷题或者面试的时候,手动模拟一下感觉可以局部最优推出整体最优,而且想不到反例,那么就试一试贪心

image-20240303151603656


如果找到局部最优,然后推出整体最优,那么就是贪心

贪心没有套路,说白了就是常识性推导加上举反例



image-20240303151853166



分发饼干

小饼干先喂饱小胃口

image-20240303155845927




摆动数列

image-20240303191722842



去重后会比较好做



贪心思路

image-20240303191959621



class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        //去重
        int i = 0, j = 0,last= 1001;
        for(; j < nums.size(); j++){
            if( last != nums[j]){
                nums[i] = nums[j];
                last = nums[i];
                i++;
            }
        }
        //新数组【0,i)
        int n = i;
		
        if(n == 1) return 1;
        if(n == 2 && nums[0] != nums[1]) return 2;
        if(n == 2 && nums[0] == nums[1]) return 1;
        
        int le = 0, ri ,cnt = 2; //cnt是为了把两个端点算进来
        for(i = 0 ;i < n-1; i++){
            ri = nums[i+1] - nums[i];
            if(ri * le < 0){  //一正一负时
                cnt++; //统计峰值
            }
            le = ri;
        }
        return cnt;
    }
};



最大子数组和

一开始拿到这道题的时候,时想着滑动窗口来做来着,但是后面想了想不行,回去看看了以前的滑动窗口题发现他们特征如下

  • 都是‘最小’ 的 – 本题是最大
  • 在符合条件的时候才会移动左指针 – 没有所谓的条件



贪心:

贪心的思路为局部最优:当前“连续和”为负数的时候立刻放弃,从下一个元素重新计算“连续和”,因为负数加上下一个元素 “连续和”只会越来越小。从而推出全局最优:选取最大“连续和”

image-20240303195336813



买股票的最佳时机II

只收集每天的正利润,最后稳稳的就是最大利润

image-20240303201944509



跳跃游戏

关键是理解跳跃的范围

image-20240303205717001



*跳跃游戏

还是比较巧的,细节挺多的,容易踩坑拿不到满分 – last 和 ans的初值

真正解题的时候,要从覆盖范围出发,不管怎么跳,覆盖范围内一定是可以跳到的,以最小的步数增加覆盖范围,覆盖范围一旦覆盖了终点,得到的就是最少步数!

这里需要统计两个覆盖范围,当前这一步的最大覆盖和下一步最大覆盖



如果移动下标达到了当前这一步的最大覆盖最远距离了,还没有到终点的话,那么就必须再走一步来增加覆盖范围,直到覆盖范围覆盖了终点。

关键是理解覆盖范围,需要好好的品

image-20240304190612534



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

贪心的思路,局部最优:让绝对值大的负数变为正数,当前数值达到最大,整体最优:整个数组和达到最大。

image-20240304201603444



[ 加油站]

是一道不好理解的题

当totalsum大于0说明肯定有一种解法能走完 当做一个结论吧,可以举个例子

image-20240305113729850



分发糖果

第一次遇见这种类型的题 一开始感觉还是比较难的

主要就是要分成两个过程来看,不要想着一下子就能解决它

还要注意for循环的边界(if) 确保所有想访问到的元素都能访问到

image-20240305155722098


这道题目一定是要确定一边之后,再确定另一边,例如比较每一个孩子的左边,然后再比较右边,如果两边一起考虑一定会顾此失彼




image-20240305155908646



柠檬水

很简单一道题哈哈哈

image-20240305161720692



*根据身高重建队列

很少做这种题,很巧,也是一个两个维度的题,不过很难想到是两个维度



本题有两个维度,h和k

image-20240306173532134



如果按照k来从小到大排序,排完之后;,会发现k的排列并不符合条件,身高也不符合条件,两个维度哪一个都没确定下来。

那么按照身高h来排序呢,身高一定是从大到小排(身高相同的话则k小的站前面),让高个子在前面。

意思就是说安排k排序,再插入元素,这样的话k就乱了,身高也是

但是先根据身高排序,因为我们遍历的时候是按照身高来遍历的,后面插入的元素即使插入到我们新数组的前面,也不会影响k(因为插入的h肯定比新数组的值都小)



image-20240306173747279


vector.insert(xx.beging()+插入的下标,要插入的元素) – vector插入还是比较慢的用链表会更快

image-20240306173236553



小总结

image-20240307112558307



image-20240307112645008



用最小数量的箭引爆气球

判断重叠区间问题

一开始想到贪心了,但是不知道该怎么实现,也不知道这个贪心是否真的可以ac(没考虑到连环重合)

局部最优:当气球出现重叠,一起射,所用弓箭最少。全局最优:把所有气球射爆所用弓箭最少。

image-20240307232034334



image-20240307231752761



image-20240307232242213



无重叠区间

和上一题差不多,直接秒了!哈哈哈

我的思路:排序后,两个临近有重叠的区间肯定要删一个,然后我们 ‘删’ 右区间大的区间

image-20240307235404726



划分字母区间

不是很难哈哈哈

我的贪心思路:让尽可能少的字母(种类)在一个片段中,一达到分割条件 就马上分割

image-20240308113016705



老师的思路:

image-20240308113642996

image-20240308113652155



合并区间

也是成功的a了出来

也是一道重叠问题,好像重叠问题很多都要排序,最后都要处理一下最后一步(循环从[0,size)

我的思路: 就是把覆盖的都合并了,然后再加入ans

image-20240308153343826

class Solution {
public:
    vector<vector<int>> ans;
    vector<int> area;
    static bool cmd(vector<int>& x, vector<int>& y){
        if(x[0] != y[0]) return x[0] < y[0];
        return x[1] < y[1];
    }
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        if(intervals.size() == 1) return intervals;
        sort(intervals.begin(), intervals.end(), cmd);
        int left = 0,flag = 0;
        
        for(int i=0;i<intervals.size()-1 ;i++){ 
            if(flag == 0) left = intervals[i][0]; //表明上次不是重叠的区间,可以更新  
            if(intervals[i][1] >= intervals[i+1][0]){  //重叠时
                intervals[i+1][1] = max(intervals[i+1][1], intervals[i][1]);//取最大的
                flag++;  //保留left的值
            }else{
                area.push_back(left),area.push_back(intervals[i][1]);
                ans.push_back(area);
                area.clear();
                flag = 0; //可以更新left的值了
            }
        }
        //特殊处理最后一个区间的情况
        if(flag != 0){  //重叠时
            area.push_back(left),area.push_back(intervals[intervals.size()-1][1]);
            ans.push_back(area);
        }else{  //不重叠时
            ans.push_back(intervals[intervals.size()-1]);
        }
        return ans;

    }
};



老师代码,真的是很简洁啊

老师思路:第一个区间直接加(自己肯定不重叠),遇到重叠的直接更新答案,不重叠的直接加入ans

少了边界处理,真的很优雅QAQ



单调递增的数字

没a出来 主要有两个原因吧

  • 贪心错了,没考虑到 200 -> 190的情况 – mark的后面应该都为9
  • 不知道前一位减减后 ,前导0怎么处理 – 当时想复杂了



思路还是很巧的

image-20240309233800213



还学到两个很重要的API

to_string – 将 int转为string

stoi – 将string转为int

image-20240309233048263



小总结

image-20240310114324876

image-20240310114536755



img

局部最优 - > 全局最优!!






此文章用于笔者记录学习,也希望对你有帮助

  • 33
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值