贪心算法(leetcode分类解题,C++代码详细注释)

前言

贪心算法或贪心思想采用贪心的策略,保证每次操作都是局部最优的,从而使最后得到的结果是全局最优的。

455.分发饼干

在这里插入图片描述
题解
因为饥饿度最小的孩子最容易吃饱,所以我们先考虑这个孩子。为了尽量使得剩下的饼干可以满足饥饿度更大的孩子,所以我们应该把大于等于这个孩子饥饿度的、且大小最小的饼干给这个孩子。满足了这个孩子之后,我们同样的策略,考虑剩下孩子里饥饿度最小的孩子,直到没有满足条件的饼干存在。

简而言之,这里的贪心策略是,给剩余孩子里最小饥饿度的孩子分配最小的能饱腹的饼干。

至于具体实现,因为我们需要获得大小关系,一个便捷的方法就是把孩子和饼干分别排序。

这样我们就可以从饥饿度最小的孩子和大小最小的饼干出发,计算有多少个对子可以满足条件。


注意: 对数组或字符串排序是常见的操作,方便之后的大小比较。

注意: 在之后的讲解中,若我们谈论的是对连续空间的变量进行操作,我们并不会明确区分数组和字符串,因为他们本质上都是在连续空间上的有序变量集合。一个字符串“abc”可以被看作一个数组 [‘a’,‘b’,‘c’]。

C++代码

class Solution {
public:
    int findContentChildren(vector<int>& children, vector<int>& cookies) {
        sort(children.begin(), children.end());
        sort(cookies.begin(), cookies.end());
        int child = 0, cookie = 0;
        //初始化两个指针,一个指向第一个小孩,另一个指向第一个饼干。
        while (child < children.size() && cookie < cookies.size()) //指针在正常范围内,运行
        {
            if (children[child] <= cookies[cookie]) ++child;
            //如果第cookie个cookies 比第child个childern大,说明第child个childern可以被满足,于是小孩与饼干指针同时向后移动。
            ++cookie;
            //如果饼干不能满足小孩,那么饼干指针向下移
        }
        //直到某个指针超范围,返回满足的小孩数
    	return child;

    }
};

135.分发糖果

在这里插入图片描述
解题
虽然这道题leetcode规定为困难,但我觉得这道题是贪心算法中简单的一种。
首先,我们需要初始化一个全为1的数组,从右向左遍历数组,如果左边比右边大,那么左边就在右边基础上加一;再从左向右遍历数组,如果右边比左边大,右边就在左边的基础上加一;
这里的贪心策略就是只考虑相邻两位的关系

代码

class Solution {
public:
    int candy(vector<int>& ratings) {
        int size = ratings.size();
        if(size<2)
        {
            return size;
        }
        //如果只有一个,那么就直接返回1就可以
        vector<int> nums(size,1);
        //初始化全为一的数组
        for(int i = 1;i<size;++i)
        //从左向右扫描,如果右边比左边大,那么右边在左边的基础上加一
        {
            if(ratings[i]>ratings[i-1])
            {
                nums[i]=nums[i-1]+1;

            }
        }
        for(int i = size-1;i>0;--i)
        //从右边向左边扫描,如果右边比左边大,再比较一下是本身大,还是右边加一大。
        {
            if(ratings[i-1]>ratings[i])
            {
                nums[i-1]= max(nums[i-1],nums[i]+1);
            }
        }
        return accumulate(nums.begin(), nums.end(), 0);
        //返回数组中所有元素的和
    }
};

435.无重叠区间

在这里插入图片描述

解题
在选择要保留区间时,我们应该注意区间的结尾,我们选择的区间结尾越小,那么我们保留下来的区间就越多,因此我们采取的贪心策略为,优先保留结尾小且不相交的区间。

那么如何实现呢?
首先,先把区间按照结尾的大小排序;然后,每次选取结尾最小的且和前一个选择的区间不重叠的区间。这就需要用到C++ 的lambda,并结合std::sort()函数进行自定义排序

代码

class Solution {
public:
    int eraseOverlapIntervals(vector<vector<int>>& intervals) 
    {
        if(intervals.empty())
        {
            return 0;
        }
        int n = intervals.size();
        sort(intervals.begin(),intervals.end(),[](vector<int> a,vector<int> b){return a[1] < b[1];});
        //C++ lambda函数自定义排序,如果看不懂,请移步C++基础
        int abandon  = 0,prev = intervals[0][1];
        //初始化prev指针指向第一组第二个数
        for(int i = 1;i<n;++i)
        {
            if(intervals[i][0]<prev)
            //如果后一组第一个数小于前一组第二个数,说明区间有重复,需要舍弃后一组(原因建见题解),所以abandon加一。
            {
                ++abandon;
            }
            else
            {
                prev = intervals[i][1];
            }
            //如果后一组第一个数大于这一组第二个数,那么prev指向这一组。
        }
        return abandon;


    }
};

605.种花问题

在这里插入图片描述

题解
这道题的贪心策略就是能种就种

代码

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+1 == flowerbed.size() || flowerbed[i+1] == 0)
            && (i == 0 || flowerbed[i-1] == 0))
            //判读是否能种,能种就种。首先当前位置是0;其次,当前位置是末尾或者下一个位置是0;然后,当前位置是首位,或者前一个位置是0。这样的位置你就可以欢乐的种花花了。
            {
                flowerbed[i]= 1;
                ans += 1;
                //种花位置加1
            }
        }
        return ans >= n;
    }
};

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

在这里插入图片描述
题解
这道题看着挺长,其实很简单
leetcode 官方的图,嘿嘿。

注意这是按末尾坐标排序的
排序后如何放箭就显而易见了,第一把箭放在黄色框末尾,第二把箭放在蓝色框框末尾

代码

class Solution {
public:
    int findMinArrowShots(vector<vector<int>>& points) {
        if (points.empty()) {
            return 0;
        }
        sort(points.begin(), points.end(), [](const vector<int>& u, const vector<int>& v) {
            return u[1] < v[1];
        });
        //通过C++ lambda函数对每组坐标以末尾进行排序
        int pos = points[0][1];
        //初始化第一把箭的位置为第一个气球右坐标。
        int ans = 1;
        //如果有气球,一定会使用一支箭。
        for (const vector<int>& balloon: points) {
            if (balloon[0] > pos)
            //如果某个气球的左坐标大于箭的位置,说明它已经超出了第一支箭所能射击的范围,所以箭数加一 ,并将这个气球的右坐标设置为喜下一支箭的初始位置。
            {
                pos = balloon[1];
                ++ans;
            }
        }
        return ans;
        //返回箭数
    }
};

763.划分字母区间

在这里插入图片描述
题解
遍历字符串,通过哈希表或者数组记录每个字母最后一次出现的位置;再遍历字符串,设遍历第 i i i个字母,且当前字母最后出现的位置为 e n d i endi endi e n d = m a x ( e n d , e n d i ) end = max(end,endi) end=max(end,endi),当 i = e n d i=end i=end时说明前 i + 1 i+1 i+1为一个符合题意的片段;初始化 s t a r t start start e n d + 1 end+1 end+1,重复上述操作
如果理解不了,可以画画,就明白了。

代码

class Solution {
public:
    vector<int> partitionLabels(string S) 
    {
        int last[26];
        //此数组将用于保存每个字母最后出现的位置
        int length =S.size();
        for(int i =0;i<length;++i)
        {
            last[S[i]-'a'] = i;
            //记录每个字母最后出现的位置
        }
        vector<int> partition;
        int start = 0,end =0;
        for(int i =0;i<length;++i)
        {
            end = max(end,last[S[i]-'a']);
            //寻找边界
            if(i == end )
            {
                partition.push_back(end-start+1);
                start = end +1;
            }
        }
        return partition;
    }
};

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

在这里插入图片描述
题解
贪心策略:涨了就卖。至于为什么第二天涨了就卖出呢,因为在持续增长的情况下接连卖出和涨到最高点卖出获利是一样的,所以只要第二天涨了就卖出。

代码

//太简单了,实在是没什么好注释的!!!!
class Solution {
public:
    int maxProfit(vector<int>& prices) 
    {
        int res =0;
        for(int i =1; i < prices.size();++i)
        {
            if(prices[i]>prices[i-1])
            {
                res += prices[i] -prices[i-1];
            }
        }
        return res;

    }
};

376. 摆动序列

在这里插入图片描述

题解

代码

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) 
    {
        if(nums.size()<2) return nums.size();
        int cur =0;
        int pre =0;
        int result =1;
        for(int i =1;i<nums.size();++i)
        {
            cur = nums[i]-nums[i-1];
            if((pre >= 0 && cur <0) || (pre<=0 && cur>0))
            {
                ++result;
                pre =cur;
            }
        }
        return result;

    }
};

53. 最大子序和

在这里插入图片描述

代码
先来个暴力算法

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int result = INT32_MIN;
        int count = 0;
        for (int i = 0; i < nums.size(); i++) { // 设置起始位置
            count = 0;
            for (int j = i; j < nums.size(); j++) { // 每次从起始位置i开始遍历寻找最大值
                count += nums[j];
                result = count > result ? count : result;
            }
        }
        return result;
    }
};

贪心算法


持续更新中

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值