阅读完本文,你将对贪心算法有初步的认知和刷题能力。
贪心算法
用一句话概括:每一步都选局部最优解。
对于结果是不是最优解并不关心。因为对于全局来说,局部的每一步最优解累计起来,可能是最优解,也可能不是最优解。
因为贪心算法没有固定的模板,只是一种思想,所以本文收录了几道简单和中等难度的题目,用于认识和理解贪心算法。
贪心算法和贪吃蛇
我们用贪吃蛇的例子来感受贪心算法:
如果每一步都选择最优解,蓝色路线是贪心算法选出来的路线,而作为人类,我们一眼就能看出,红色路线是最优解。这个例子就是贪心算法不是最优解的例子。

Easy / 简单
Leetcode 121 - 买卖股票的最佳时机
首先来一道HOT100上贪心算法板块的简单题目。
本题的想法是,找最低的价格买入,找最高的价格卖出,求局部最优解,以获得全局最优解。
cpp
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
int minPrice = INT_MAX;
int maxProfit = 0;
for(int i = 0;i<n;i++){
minPrice = min(minPrice,prices[i]);
maxProfit = max(maxProfit,prices[i] - minPrice);
}
return maxProfit;
}
};
Leetcode 455 - 分发饼干
两列数据按照顺序排列后,注意越界判断,可以写在for循环里:
cpp
for (; ck < cookies.size() && cd < childs.size(); ck++)
也可以写在if里,但注意要在判断大小之前。如下:
cpp
class Solution {
public:
int findContentChildren(vector<int>& childs, vector<int>& cookies) {
sort(cookies.begin(), cookies.end(), less<int>());
sort(childs.begin(), childs.end(), less<int>());
int ck = 0;
int cd = 0;
for( ;ck<cookies.size();ck++){
if(cd <childs.size() && childs[cd] <= cookies[ck]){
cd++;
}
}
return cd;
}
};
Leetcode 860 - 柠檬水找零
找零场景分为三种情况,顾客支付5,10,20 。5和10都比较简单,注意20时,当10的值为0时,考虑找零3张5 。
cpp
class Solution {
public:
bool lemonadeChange(vector<int>& bills) {
int five = 0;
int ten = 0;
for (int bill : bills) {
if (bill == 5) {
five++;
} else if (bill == 10) {
ten++;
if (five) {
five--;
} else {
return false;
}
} else {
if (ten && five) {
ten--;
five--;
} else if (five >= 3) {
five -= 3;
} else {
return false;
}
}
}
return 1;
}
}
;
Medium / 中等
Leetcode 376 摆动序列
cpp
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
int n = nums.size();
if(n<2){
return n;
}
int preDiff = nums[1] - nums[0];
int res = preDiff != 0? 2:1;
for(int i = 2;i<n;i++){
int diff = nums[i] - nums[i-1];
if((diff > 0 && preDiff <=0)|| (diff < 0 && preDiff >=0)){
res++;
preDiff = diff;
}
}
return res;
}
};
Leetcode 738 - 单调增加的数字
首先尝试暴力法:
cpp
class Solution {
public:
bool isMono(int n){
int pre = 10;
while(n){
int dig = n % 10;
if(dig > pre) return false;
pre = dig;
n = n / 10;
}
return true;
}
int monotoneIncreasingDigits(int n) {
while(n){
if(isMono(n)){
return n;
}
n--;
}
return 0;
}
};
发现不转换为字符串,是非常难解决的,尝试转换为字符串,例如85,最后结果是79,可以发现是if(si-1>si),需要把si-1--,且si=9。
再多举几个例子,例如8765,其中的过程是:8765->8759->8699->7999。故写出以下代码:
cpp
class Solution {
public:
int monotoneIncreasingDigits(int n) {
string s = to_string(n);
for(int i = s.size()-1;i>0;i--){
if(s[i-1]> s[i]){
s[i-1]--;
for(int j = i;j<s.size();j++) s[j] = '9';
}
}
return stoi(s);
}
};