leetcode——我贪之贪得无厌之不能再贪之贪心算法(c++系列题目)

  • 我来填坑了,这篇博客给大家带来的是我贪之贪得无厌之不能再贪之贪心算法系列题目,期中很多到题我都是搬的题解,主要是理解其中的思路,如有侵权,联系侵删啊。
  • 建议大家自己还是手动去刷一下这些题,这些题都比较经典,手刷一遍印象更深。
  • 还有强烈大家关注406和763,这两题不是一般的贪,而是那种神奇巧妙的贪,我愿称其为贪中之贪

455. 分发饼干

在这里插入图片描述

  • 贪心,直接给两个数组排序(从小到大),依次比较即可,如果当前饼干小于当前孩子胃口,说明该饼干一个都满足不了,直接用下块跟当前孩子比即可,直到两个数组有一个遍历完即可。
class Solution {
public:
    int findContentChildren(vector<int>& g, vector<int>& s) {
        if( g.size() == 0 || s.size() ==0) 
            return 0;
        sort(g.begin(),g.end());
        sort(s.begin(),s.end());
        int gi = 0,si = 0;
        while(gi < g.size() && si < s.size()){
            if(g[gi] <= s[si]){
                gi++;
            }
            si++;
        }
        return gi;
    }
};

435. 无重叠区间

在这里插入图片描述
在这里插入图片描述

  • 贪! 将区间按结束时间从小到大排序,获取最小的区间终结点,如果区间的起点,小于上一个区间的终点,说明有交集,要删除;没有交集,更新终结点。
  • 注意: Leecode的sort的bool要加static变量,否则会报错
class Solution {
public:

    static bool cmp(vector<int> a, vector<int> b){
        return a[1] < b[1];
    }

   int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        if(intervals.size() == 0)
            return 0;
        //按照区间终结点,从小到大排序
        sort(intervals.begin(),intervals.end(),cmp);
        int end = intervals[0][1];
        int ans = 0;
        for(int i = 1 ; i < intervals.size(); i++){
            if(intervals[i][0] < end){
                ans++;
            }
            else{
                end = intervals[i][1];
            }
        }
        return ans;
    }
};

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

在这里插入图片描述

  • 该题也是计算不重叠的区间个数,不过和上题的区别在于,[1, 2] 和 [2, 3] 在本题中算是重叠区间。思路和上题一样,判断条件不同即可。
class Solution {
public:
    static bool cmp(vector<int> a, vector<int> b){
        return a[1] < b[1];
    }

    int findMinArrowShots(vector<vector<int>>& points) {
        if(points.size()==0){
            return 0;
        }
        sort(points.begin(),points.end(),cmp);
        int ans = 1;
        int end = points[0][1];
        for(int i = 1; i < points.size(); i++){
            if(points[i][0] > end){
                ans++;
                end = points[i][1];
            }
        }
        return ans;
    }
};

406. 根据身高重建队列

在这里插入图片描述

  • 这个题贪的有点意思啊,因为个子矮的人相对于个子高的人是看不见的。。。是看不见的。。。不见的。。。 因此后面可以按序插入,经典啊,牛逼啊,是不是没想到
  • 先按身高h降序排,再按k升序排,然后依次插入到k下标即可,是不是很神奇,我也觉的很神奇,学到了。

防止某些同学学不会,我直接上图手把手交你贪啊要个赞不过分吧
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    //先按身高h降序排,再按k升序排
    static bool cmp(vector<int> a, vector<int> b){
        if(a[0] == b[0])
            return a[1] < b[1];
        return a[0] > b[0];
    }
    vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
        sort(people.begin(),people.end(),cmp);

        //使用链表可以优化性能
        list<vector<int>> r;
        for (auto& e : people) {
            auto pos = r.begin(); //lsit不支持随机访问
            advance(pos, e[1]);   //使迭代器前进或后退n个位置。
            r.insert(pos, e);
        }
       
        return std::vector<vector<int>>(r.begin(), r.end());
        
		//下面是vector板的插入,性能没有链表好
        // vector<vector<int>> res;
        // for (auto& e : people) 
        //       res.insert(res.begin() + e[1], e);
        // return res;
    }
};

121. 买卖股票的最佳时机

在这里插入图片描述

  • 别想,贪!!! 默认将第一天当成股票买入最低点,从第二天开始遍历,如果该天比最低点小,记录该点即可,否则用看今天的收益是否大于最大值,大于则替换。遍历一便结果就出来了。注意会给空数组的情况。
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(prices.size() == 0) return 0;
        int ans = 0;
        int minx = prices[0];
        for(int i = 1 ; i < prices.size(); i++){
            if(prices[i] < minx){
                minx = prices[i];
            }
            else if(prices[i] - minx > ans){
                ans = prices[i] - minx;
            }
        }
        return ans;
    }
};

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

在这里插入图片描述
在这里插入图片描述

  • 别问,问就是贪,原则就是所有上涨交易日都买卖(赚到所有利润),所有下降交易日都不买卖(永不亏钱)。(加所有的上升段)
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int ans = 0;
        for(int i = 1; i < prices.size() ; i++){
            int x = prices[i] - prices[i-1];
            if(prices[i] - prices[i-1] > 0){
                ans += prices[i] - prices[i-1];
            }
        }
        return ans;
    }
};

605. 种花问题

在这里插入图片描述
在这里插入图片描述

  • 这个题应该算贪吧,我们从左到右扫描数组,如果数组中有一个 0,并且这个 0 的左右两侧都是 0,那么我们就可以在这个位置种花,即将这个位置的 0 修改成 1,并将计数器 count 增加 1。
  • 注意:对于数组的第一个和最后一个位置,我们只需要考虑一侧是否为 0
class Solution {
public:
    bool canPlaceFlowers(vector<int>& flowerbed, int n) {
        int ans = 0;
        for(int i = 0; i < flowerbed.size();i++){
            if(flowerbed[i] == 0 && ( i == 0 || flowerbed[i - 1] == 0 ) && 
           		  ( i == flowerbed.size()-1 || flowerbed[i + 1] == 0 )){
                flowerbed[i] = 1;
                ans++;
            }
            if(ans >= n)
               return true;
        }
        return false;
    }
};

665. 非递减数列

在这里插入图片描述

  • 这道题给了我们一个数组,说我们最多有1次修改某个数字的机会,
  • 问能不能将数组变为非递减数组。题目中给的例子太少,不能覆盖所有情况,我们再来看下面三个例子:

4,2,3 -> 2,2,3
1,4,2,3 -> 1,2,2,3
2,3,3,2,4 -> 2,3,3,3,4

  • 我们通过分析上面三个例子可以发现,当我们发现后面的数字小于前面的数字产生冲突后,有的时候改前面的数字,有点时候改后面的数子,两者直接有什么关系呢?贪,往前在看一个数字:
  • 首先如果再前面的数不存在,改前面,比如例子[1],4前面没有数字了,我们直接修改前面的数字为当前的数字2即可。
  • 而当再前面的数字存在,并且小于当前数时,改前面,比如例子[2],1小于2,我们还是需要修改前面的数字4为当前数字2;
  • 如果再前面的数大于当前数,改后面,比如例子[3],3大于2,我们需要修改当前数2为前面的数字3。
class Solution {
public:
    bool checkPossibility(vector<int>& nums) {
        int cnt = 0;
        for(int i = 1; i < nums.size() && cnt < 2; i++){
            if(nums[i-1] <= nums[i]){
                continue;
            }
            cnt++;
            if(i - 2 >= 0 && nums[i-2] > nums[i]){
                nums[i] = nums[i-1];
            }else{
                nums[i-1] = nums[i];
            }
        }
        return cnt < 2;
    }
};

53. 最大子序和

在这里插入图片描述

  • 直接贪,从左往右迭代,一个个数字的加过去,如果sum<0;重当前开始找字串。
  • 如上面的例子,默认sum=num[0],从下标为1开始。当下标为1时,sum = -2小于0,所以从sum=num[1];下标为2时,sum=1大于0,所以sum+=nums[2]直接加上当前值。依次类推。这还听不懂的话,直接上图再次手把手交你贪啊

这里是引用
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        if(nums.size()==0)
            return 0;
        int preSum  = nums[0];
        int ans = preSum;
        for(int i = 1;i < nums.size();i++){
                preSum = preSum > 0?preSum + nums[i]:nums[i];
                ans = max(preSum,ans);
        }
        return ans;
    }
};

763. 划分字母区间

在这里插入图片描述

  • 这道题,是真的贪,而且贪的还很巧妙很神奇,我愿称其为贪中之经典。
  • 思路如下: 注意题目要求啊,要尽可能的划分更多的片段,先确定思路:看见开头一个字母,就要看这个字母最后出现在哪,然后看看这个开头和结尾之间的那些其他字母是不是都落在这个区间里,如果不是,则扩充区间,如果是,那就最好了
  • 对于这道题,一个关键点是利用数组记载一个字母最后出现的位置index。
  • 然后,遍历字符串并跟它比较,用max函数来看看是边界大还是当前遍历的这个字母的最大index大
  • 如果当前遍历字母的最大index大,那就说明超过了边界,那就要更新边界,大概就是这个思路。 结合我说的,康康下面的代码哈。
class Solution {
public:
    vector<int> partitionLabels(string S) {
        int len = S.length();
        vector<int> ends(26,-1);
        //记录每个字母最后出现的次数。
        for(int i = 0; i < len; i++){
            ends[S[i]-'a'] = i;
        }
        vector<int> ans;
        int i = 0;
        while(i < len){
            //记录第一个字符的最后出现的位置
            int r = ends[S[i] - 'a']; 
            //遍历第一个字符到最后一个字符间的每个字符是否都在区间内     
            for(int j = i; j <= r; j++){
                //不在区间内,则更新区间
                if(ends[S[j]-'a'] > r){
                    r = ends[S[j]-'a'];
                }
            }
            ans.push_back(r - i + 1);
            i = r + 1;
        }
        return ans;
    }
};

看完了,学会了没?点个赞呗。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值