贪心算法或贪心思想采用贪心的策略,保证每次操作都是局部最优的,从而使最后得到的结果是全局最优的。
题解
因为饥饿度最小的孩子最容易吃饱,所以我们先考虑这个孩子。为了尽量使得剩下的饼干可以满足饥饿度更大的孩子,所以我们应该把大于等于这个孩子饥饿度的、且大小最小的饼干给这个孩子。满足了这个孩子之后,我们同样的策略,考虑剩下孩子里饥饿度最小的孩子,直到没有满足条件的饼干存在。
简而言之,这里的贪心策略是,给剩余孩子里最小饥饿度的孩子分配最小的能饱腹的饼干。
至于具体实现,因为我们需要获得大小关系,一个便捷的方法就是把孩子和饼干分别排序。
这样我们就可以从饥饿度最小的孩子和大小最小的饼干出发,计算有多少个对子可以满足条件。
注意: 对数组或字符串排序是常见的操作,方便之后的大小比较。
注意: 在之后的讲解中,若我们谈论的是对连续空间的变量进行操作,我们并不会明确区分数组和字符串,因为他们本质上都是在连续空间上的有序变量集合。一个字符串“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;
}
};
解题
虽然这道题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);
//返回数组中所有元素的和
}
};
解题
在选择要保留区间时,我们应该注意区间的结尾,我们选择的区间结尾越小,那么我们保留下来的区间就越多,因此我们采取的贪心策略为,优先保留结尾小且不相交的区间。
那么如何实现呢?
首先,先把区间按照结尾的大小排序;然后,每次选取结尾最小的且和前一个选择的区间不重叠的区间。这就需要用到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;
}
};
题解
这道题的贪心策略就是能种就种。
代码
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;
}
};
注意这是按末尾坐标排序的
排序后如何放箭就显而易见了,第一把箭放在黄色框末尾,第二把箭放在蓝色框框末尾
代码
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;
//返回箭数
}
};
题解
遍历字符串,通过哈希表或者数组记录每个字母最后一次出现的位置;再遍历字符串,设遍历第 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;
}
};
题解
贪心策略:涨了就卖。至于为什么第二天涨了就卖出呢,因为在持续增长的情况下接连卖出和涨到最高点卖出获利是一样的,所以只要第二天涨了就卖出。
代码
//太简单了,实在是没什么好注释的!!!!
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;
}
};
题解
代码
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;
}
};
代码
先来个暴力算法
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;
}
};
贪心算法
持续更新中