文章目录
- [1 贪心算法 (Greedy Algorithm)](#1 贪心算法 (Greedy Algorithm))
-
- [1.1 核心思想与特点](#1.1 核心思想与特点)
- [1.2 经典贪心类问题](#1.2 经典贪心类问题)
- [1.3 经典问题详解与代码实现](#1.3 经典问题详解与代码实现)
1 贪心算法 (Greedy Algorithm)
- 贪心算法是一种在每一步选择中都采取当前状态下最优(最有利)的选择,从而希望导致结果是全局最优的算法策略。
1.1 核心思想与特点
- 核心特征:
- 局部最优选择: 每一步都做出当前看来最好的选择
- 不可回溯: 一旦做出选择,就不再回退
- 无后效性: 当前的选择不会影响后续子问题的结构
- 贪心算法的适用条件:
- 贪心性质选择: 局部最优能导致全局最优
- 最优子结构: 问题的最优解包含子问题的最优解
1.2 经典贪心类问题
- 区间调度类问题
- 分配类问题
- 背包类问题
- 路径/图论问题
- 构造类问题
1.3 经典问题详解与代码实现
- 无重叠区间
cpp
复制代码
// LeetCode 435: 无重叠区间
// 问题:移除最少数量的区间,使剩余区间互不重叠
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
if (intervals.empty()) return 0;
// 按结束时间排序(贪心关键:早结束的给后面留更多空间)
sort(intervals.begin(), intervals.end(),
[](const vector<int>& a, const vector<int>& b) {
return a[1] < b[1];
});
int count = 0; // 需要移除的区间数
int end = intervals[0][1];
for (int i = 1; i < intervals.size(); i++) {
// 如果当前区间开始时间 < 上一个区间的结束时间,说明重叠
if (intervals[i][0] < end) {
count++; // 需要移除当前区间
} else {
end = intervals[i][1]; // 不重叠,更新结束时间
}
}
return count;
}
/*
示例:
输入: [[1,2],[2,3],[3,4],[1,3]]
排序后: [[1,2],[2,3],[1,3],[3,4]]
贪心选择:[1,2], [2,3], [3,4]
移除:[1,3]
输出: 1
*/
- 用最少数量的箭引爆气球
cpp
复制代码
// LeetCode 452: 用最少数量的箭引爆气球
int findMinArrowShots(vector<vector<int>>& points) {
if (points.empty()) return 0;
// 按结束坐标排序
sort(points.begin(), points.end(),
[](const vector<int>& a, const vector<int>& b) {
return a[1] < b[1];
});
int arrows = 1;
int end = points[0][1];
for (int i = 1; i < points.size(); i++) {
// 如果当前气球开始位置 > 上一箭的射击位置
if (points[i][0] > end) {
arrows++; // 需要新的一箭
end = points[i][1]; // 更新射击位置
}
// 否则当前箭可以射爆这个气球,继续
}
return arrows;
}
- 视频拼接
- 问题的关键在于,所有小于当前右节点的所有左节点中,右节点最长的那个
cpp
复制代码
// LeetCode 1024: 视频拼接
int videoStitching(vector<vector<int>>& clips, int time) {
// 按开始时间排序,开始时间相同的按结束时间降序
sort(clips.begin(), clips.end(),
[](const vector<int>& a, const vector<int>& b) {
if (a[0] == b[0]) return a[1] > b[1];
return a[0] < b[0];
});
int count = 0;
int curEnd = 0, nextEnd = 0;
int i = 0, n = clips.size();
while (i < n && clips[i][0] <= curEnd) {
// 在当前可覆盖的区间内,找能延伸到最远的
while (i < n && clips[i][0] <= curEnd) {
nextEnd = max(nextEnd, clips[i][1]);
i++;
}
count++;
curEnd = nextEnd;
if (curEnd >= time) {
return count;
}
}
return -1; // 无法覆盖整个区间
}
- 分发饼干
cpp
复制代码
// LeetCode 455: 分发饼干
int findContentChildren(vector<int>& g, vector<int>& s) {
// g: 孩子的胃口值,s: 饼干的尺寸
sort(g.begin(), g.end());
sort(s.begin(), s.end());
int child = 0, cookie = 0;
while (child < g.size() && cookie < s.size()) {
if (s[cookie] >= g[child]) {
child++; // 当前饼干能满足当前孩子
}
cookie++; // 无论是否满足,饼干都要被考虑
}
return child; // 满足的孩子数量
}