贪心算法
- 前言
- 455.分发饼干
- 135.分发糖果
- 435.无重叠区间
- 605.种花问题
- 452.用最小数量的箭引爆气球
- 763.划分字母区间
- 122. 买卖股票的最佳时机 II
- 376. 摆动序列
- 53. 最大子序和
前言
贪心算法或贪心思想采用贪心的策略,保证每次操作都是局部最优的,从而使最后得到的结果是全局最优的。
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.用最小数量的箭引爆气球
题解
这道题看着挺长,其实很简单
注意这是按末尾坐标排序的
排序后如何放箭就显而易见了,第一把箭放在黄色框末尾,第二把箭放在蓝色框框末尾
代码
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.划分字母区间
题解
遍历字符串,通过哈希表或者数组记录每个字母最后一次出现的位置;再遍历字符串,设遍历第iii个字母,且当前字母最后出现的位置为endiendiendi,end=max(end,endi)end = max(end,endi)end=max(end,endi),当i=endi=endi=end时说明前i+1i+1i+1为一个符合题意的片段;初始化startstartstart为end+1end+1end+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;}
};
贪心算法
持续更新中