*题目编号为Leetcode中对应的题号。
某位大佬的Leetcode题解参考链接
贪心算法
- 贪心算法的难点在于判断一个问题是否能用贪心算法解决。
- 贪心算法离不开排序。
-
(455 分发饼干) 假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
注意:
你可以假设胃口值为正。
一个小朋友最多只能拥有一块饼干。示例 1:
输入: [1,2,3], [1,1] 输出: 1 解释: 你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。 虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。 所以你应该输出1。
示例 2:
输入: [1,2], [1,2,3] 输出: 2 解释: 你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。 你拥有的饼干数量和尺寸都足以让所有孩子满足。 所以你应该输出2.
class Solution { public: int findContentChildren(vector<int>& g, vector<int>& s) { if(g.empty() || s.empty()) return 0; // 从大到小排序 sort(g.begin(),g.end(),greater<int>()); sort(s.begin(),s.end(),greater<int>()); int gi=0, si=0; int res=0; while(gi<g.size() && si<s.size()){ if(s[si] >= g[gi]){ si++; gi++; res++; } else // s[si] < g[gi] gi++; } return res; } };
-
(392判断子序列) 给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
示例 1:
s ="abc"
, t ="ahbgdc"
返回
true
.s =
"axc"
, t ="ahbgdc"
返回
false
./// Simulation /// Time Complexity: O(len(s) + len(t)) /// Space Complexity: O(1) class Solution { public: bool isSubsequence(string s, string t) { int index = -1; for(char c: s){ index = t.find(c, index + 1); if(index == string::npos) return false; } return true; } };
-
(435 无重叠区间) 给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。
注意:
- 可以认为区间的终点总是大于它的起点。
- 区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。
示例 1:
输入: [ [1,2], [2,3], [3,4], [1,3] ] 输出: 1 解释: 移除 [1,3] 后,剩下的区间没有重叠。
示例 2:
输入: [ [1,2], [1,2], [1,2] ] 输出: 2 解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。
// 动态规划实现 bool compare(const vector<int> &a, const vector<int> &b){ if(a[0] != b[0]) return a[0] < b[0]; return a[1] < b[1]; } class Solution { public: int eraseOverlapIntervals(vector<vector<int>>& intervals) { if(intervals.empty()) return 0; sort(intervals.begin(), intervals.end(), compare); // memo[i]表示以第i个区间为结尾的最长上升子区间的长度 vector<int> memo(intervals.size(), 1); for(int i=0;i<intervals.size();i++){ for(int j=0;j<i;j++){ if(intervals[i][0]>=intervals[j][1]) memo[i]=max(memo[i], 1+memo[j]); } } int res=0; for(int i=0;i<memo.size();i++) res=max(res,memo[i]); return intervals.size()-res; } };
- 每次选择中,每个区间的结尾很重要,结尾越小,留给后面的空间越大,后面越有可能容纳更大的空间。
- 所以,按照区间的结尾排序。每次选择结尾最早的,且和前一个区间不重叠的区间。
// 贪心算法实现 bool compare(const vector<int> &a, const vector<int> &b){ if(a[1] != b[1]) return a[1] < b[1]; return a[0] < b[0]; } class Solution { public: int eraseOverlapIntervals(vector<vector<int>>& intervals) { if(intervals.empty()) return 0; sort(intervals.begin(), intervals.end(), compare); int pre=0 int res=1; for(int i=1;i<intervals.size();i++){ if(itervals[i][0]>=intervals[pre][1]){ pre=i; res++; } } return intervals.size()-res; } };
- 贪心选择性质:在求解一个最优化的过程中,使用贪心的方式选择了一组内容之后,不会影响下面的子问题的求解。或者说一个全局最优解可以通过局部最优得到,即存在一个最优解是以贪心选择开始的。
- 贪心选择性质证明:贪心算法为A,假设的最优算法为O;发现A完全能替代O,且不影响求出最优解