贪心理论基础
代码随想录链接
贪心的本质:由不断地取局部最优, 最终达到全局最优.
什么时候用贪心呢? 不好意思, 贪心不像回溯那样有固定的套路.
贪心的关键点在于通过不断地求解局部最优而达到整体最优的效果,
所以如果不能举出来局部最优影响最终的整体最优的反例,
则可以试试贪心算法.
贪心算法的一般的解题步骤: * 将问题分解为若干子问题 *
找出合适的贪心策略 * 求解每个问题的最优解 *
将局部最优解堆叠为全局最优解
455.分发饼干
代码随想录链接
题目
LeetCode-455.分发饼干
给定两个数组, 第一个数组表示孩子们的胃口,
第二个数组表示饼干们能满足的胃口的大小,
在每个孩子只能分得一块饼干的前提下, 求最多能满足几个孩子的胃口.
自己的想法
如果要满足最多的孩子, 应该从胃口最小的孩子开始,
依次取满足其胃口且最小的饼干,
这样能保证胃口小的孩子使用胃口小的饼干满足,
胃口大的孩子能够用较大的饼干满足.
题解
有两种写法, 首先是自己A出来的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class Solution { public: int findContentChildren(vector<int>& g, vector<int>& s) { vector<bool> used(s.size(), false); int satisfied = 0; sort(g.begin(), g.end()); sort(s.begin(), s.end()); for (int i = 0; i < g.size(); i++) { int sizeNeeded = g[i]; for (int j = 0; j < s.size(); j++) if (s[j] >= sizeNeeded && !used[j]) { used[j] = true; satisfied++; break; } } return satisfied; } };
|
由于孩子们胃口大小和饼干大小都进行了排序,
所以当一个位置的饼干满足了一个胃口较小的孩子之后,
下一个进行满足的孩子应得到的饼干应该在上一个孩子得到的饼干的右侧,
所以可以将两层for循环简化为一层.双指针法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Solution { public: int findContentChildren(vector<int>& g, vector<int>& s) { int satisfied = 0; sort(g.begin(), g.end()); sort(s.begin(), s.end()); int cookieIndex = 0; for (int i = 0; i < g.size(); i++) { int sizeNeeded = g[i]; while (cookieIndex < s.size() && sizeNeeded > s[cookieIndex]) cookieIndex++; if (cookieIndex < s.size() && sizeNeeded <= s[cookieIndex]) { satisfied++; cookieIndex++; } } return satisfied; } };
|
376.摆动序列
代码随想录链接
题目
LeetCode-376.摆动序列
给定一个数组, 求连续的两个数的差值是正负数交替的子序列最长的长度.
子序列在原数组中不要求一定连续.
自己的想法
先计算出来整个数组的两个数的差值, 作为一个数组,
然后再在这个数组里来找正负值交替的子序列,
最后看找出的正负值交替的子序列的最长长度.
题解
自己A出来的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| class Solution { public: int wiggleMaxLength(vector<int>& nums) { vector<int> diffs; if (nums.size() == 1) return true; int firstPositiveIndex = INT_MAX, firstNegativeIndex = INT_MAX; for (int i = 1; i < nums.size(); i++) { int diff = nums[i] - nums[i - 1]; if (diff > 0) { firstPositiveIndex = min(i - 1, firstPositiveIndex); } if (diff < 0) { firstNegativeIndex = min(i - 1, firstNegativeIndex); } diffs.push_back(diff); } vector<int> pathPos; int needPositive = true; for (int i = firstPositiveIndex; i < diffs.size(); i++) { if ((needPositive && diffs[i] > 0) || (!needPositive && diffs[i] < 0)) { pathPos.push_back(diffs[i]); needPositive = !needPositive; } } vector<int> pathNeg; needPositive = false; for (int i = firstNegativeIndex; i < diffs.size(); i++) { if ((needPositive && diffs[i] > 0) || (!needPositive && diffs[i] < 0)) { pathNeg.push_back(diffs[i]); needPositive = !needPositive; } }
return pathNeg.size() > pathPos.size() ? pathNeg.size() + 1 : pathPos.size() + 1; } };
|
感觉上面这段代码如果非要从贪心的方面来说的话,
就是为了尽可能延长子序列的长度,
就尽可能地在左侧找子序列的下一个数字.
卡哥的方法只需要遍历一遍就行, 通过对比前一个差值和当前差值是否异号.
就能判断出来最长的长度.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class Solution { public: int wiggleMaxLength(vector<int>& nums) { if (nums.size() <= 1) return nums.size(); int curDiff = 0; int preDiff = 0; int result = 1; for (int i = 0; i < nums.size() - 1; i++) { curDiff = nums[i + 1] - nums[i];
if ((preDiff <= 0 && curDiff > 0) || (preDiff >= 0 && curDiff < 0)) { result++; preDiff = curDiff; }
} return result; } };
|
贪心应该也是贪在试图在最左侧找符合条件的数字罢.
53.最大子序和
代码随想录链接
题目
LeetCode-53.最大子序和
给定一个整数数组, 找到其中和最大的连续子数组, 返回其和的值.
自己的想法
暴力法, 两个for循环
再说贪心, 局部最大, 并记录最大的连续和, 就能得到结果.
当当前的连续和下降到负数的时候, 重置连续和为0,
也就是变相更改了区间的左侧.
设另一个数result来保存结果, 并在当前连续和大于其时, 更新为当前连续和,
也是变相地更改了区间右侧.
题解
1 2 3 4 5 6 7 8 9 10 11 12
| class Solution { public: int maxSubArray(vector<int>& nums) { int sum = 0, result = INT_MIN; for (int i = 0; i < nums.size(); i++) { sum += nums[i]; result = max(sum, result); if (sum < 0) sum = 0; } return result; } };
|