顾名思义,贪心算法或贪心思想采用贪心的策略,保证每次操作都是局部最优的,从而使最后得到的结果是全局最优的。
题解
因为饥饿度最小的孩子最容易吃饱,所以我们先考虑这个孩子。为了尽量使得剩下的饼干可以满足饥饿度更大的孩子,所以我们应该把大于等于这个孩子饥饿度的、且大小最小的饼干给这个孩子。满足了这个孩子之后,我们采取同样的策略,考虑剩下孩子里饥饿度最小的孩子,直到没有满足条件的饼干存在。
简而言之,这里的贪心策略是,给剩余孩子里最小饥饿度的孩子分配最小的能饱腹的饼干。
至于具体实现,因为我们需要获得大小关系,一个便捷的方法就是把孩子和饼干分别排序。这样我们就可以从饥饿度最小的孩子和大小最小的饼干出发,计算有多少个对子可以满足条件。
代码
class Solution {
public:int findContentChildren(vector& g, vector& s) {int ans = 0;sort(g.begin(), g.end());sort(s.begin(), s.end());int i = 0;for(int biscuit : s){if(i < g.size() && biscuit >= g[i]){ans ++;i ++;}}return ans;}
};
题解
做完了题目455,你会不会认为存在比较关系的贪心策略一定需要排序或是选择?虽然这一道题也是运用贪心策略,但我们只需要简单的两次遍历即可︰
代码
class Solution {
public:int candy(vector& ratings) {int n = ratings.size();vector ans(n, 1);// 从左往右遍历for(int i=0; iif(ratings[i+1] > ratings[i]){ans[i+1] = ans[i] + 1;}}// 从右往左遍历for(int i=n-1; i>0; --i){// 左边孩子评分比右边孩子高 且 左边孩子糖果数不大于右边孩子 if(ratings[i-1] > ratings[i] && ans[i-1] <= ans[i]){ans[i-1] = ans[i] + 1;}}for(int i=0; icout<
收获
accumulate(ans.begin(), ans.end(), 0);
题解
代码
class Solution {
public:int eraseOverlapIntervals(vector>& intervals) {sort(intervals.begin(), intervals.end(), [](vector&a, vector&b){return a[1]if(intervals[i][0] < prev){ans ++;}else{prev = intervals[i][1];}}return ans;}
};
收获
sort(intervals.begin(), intervals.end(), [](vector&a, vector&b){return a[1]
题解
代码
class Solution {
public:bool canPlaceFlowers(vector& flowerbed, int n) {int len = flowerbed.size();for(int i=0; iint prev = i == 0? 0 : flowerbed[i-1];int later = i == len-1? 0 : flowerbed[i+1];if(flowerbed[i] == 0){if(!prev && !later){n-- ;flowerbed[i] = 1;}}if(flowerbed[i] == 1)++i;}return n<=0;}
};
收获
题解
if(points[i][0] <= prev)
,需要满足当前区间的左边界 <= 上一区间的右边界。 prev = min(prev, points[i][1]);
。 prev = points[i][1];
。代码
class Solution {
public:int findMinArrowShots(vector>& points) {int ans = 1;sort(points.begin(), points.end());int prev = points[0][1];for(int i=1; iif(points[i][0] <= prev){prev = min(prev, points[i][1]);}else{ans ++;prev = points[i][1];}}return ans;}
};
收获
题解
s.find_first_of(ch)
找到该字符第一次出现的位置,记为 begin ,使用函数 s.find_last_of(ch)
找到该字符最后一次出现的位置, 记为 end ,这样就划分出了一个片段。pos = s.find_last_of(ch)
来确定该字符最后一次出现的位置,并与 end 做比较,如果end < pos, 说明该字符在该片段后还有出现,此时需要更新 end ,扩大片段范围 ,end = max((int)s.find_last_of(s[i]), end);
。cnt
来记录字符是否出现过。ch = s[end + 1];
。代码
class Solution {
public:vector partitionLabels(string s) {vector ans;vector cnt(26);char ch = s[0];cnt[ch - 'a'] = 1;while(1){int start = s.find_first_of(ch);int end = s.find_last_of(ch);for(int i=start+1; i<=end; ++i){if(!cnt[s[i] - 'a']){// 说明还未遍历end = max((int)s.find_last_of(s[i]), end);// int newEnd = s.find_last_of(s[i]);// if(newEnd > end) end = newEnd;cnt[s[i] - 'a'] = 1;}}ans.push_back(end - start + 1);if(end == s.size() - 1) return ans;ch = s[end + 1];}}
};
收获
s.find_first_of()
和 s.find_last_of()
,但是它们的返回类型为size_type ,并不是完全等同于 int ,因此在判断是否更新 end 的时候,需要将返回值强制转换成 int ,才可以使用 max 函数。
题解
prev = prices[i];
,注意 ,由于题目中提到在当天抛售完这支股票后可以再次买入,因此无论当天是否售出股票,都要更新 prev 为当天的价格。代码
class Solution {
public:int maxProfit(vector& prices) {int ans = 0;int n = prices.size();int prev = prices[0];for(int i=1; iif(prices[i] > prev){ans += prices[i] - prev;}prev = prices[i];}return ans;}
};
收获
单独交易日,当天买入,隔天售出:profit = prices[i] - prices[i-1];
连续上涨交易日,当天买了,隔几天才售出,profit = prices[i] - prices[j] = (prices[i] -prices[i-1]) + (prices[i-1] - prices[i-2]) + ... + (prices[j+1] - prices[j])
这个公式佐证了我的题解的正确性。
题解
代码
class Solution {
public:vector> reconstructQueue(vector>& people) {// 按照第一维度 h 降序,第二维度 k 升序sort(people.begin(), people.end(), [](const vector &a, const vector &b){if(a[0] == b[0]) return a[1] < b[1];return a[0] > b[0];});vector> ans;for(auto p : people){if(p[1] < ans.size()){// 插入ans.insert(ans.begin() + p[1], p);}else ans.push_back(p);}return ans;}
};
收获
ans.insert(ans.begin() + p[1], p);
;
题解
nums[i] <= nums[i+1]
即可。 对于这道题,其实就是求出将该数组变成非递减数列所需要的最小次数,那么为了使得元素的改变次数最小,对于不满足题目要求的元素,如nums[i] > nums[i+1]
,我们就将其改为 nums[i] = nums[i+1]
。nums[i] > nums[i+1]
这种情况,我们既可以改变 nums[i]
,也可以改变 nums[i+1] 。因此我们分成两种情况考虑: nums[i+1]
;nums[i]
。代码
class Solution {
public:bool checkPossibility(vector& nums) {int cnt1 = 0, cnt2 = 0;int n = nums.size();vector copy(n);copy = nums;for(int i=0; iif(nums[i] > nums[i+1]){++ cnt1;nums[i+1] = nums[i]; } }for(int i = n-1; i>0; --i){if(copy[i] < copy[i-1]){++ cnt2;copy[i-1] = copy[i]; }}return min(cnt1, cnt2) <= 1;}
};
收获
if (nums[i] > nums[i + 1]) // 出现递减
{if (flag) // 如果还有修改机会,进行修改{if (nums[i + 1] >= nums[ i - 1])// 修改方案1nums[i] = nums[i + 1];else // 修改方案2nums[i + 1] = nums[i]; flag = false; // 用掉唯一的修改机会}
else // 没有修改机会,直接结束return false;
用了三天才把贪心算法学完,确实不太难,而且代码也相对简练,练习题中只有 406 我是看了题解才明白,不过 655 的另外一种思路也很值得我借鉴。
总的来说有三种题型: