3步掌握贪心算法:从LeetCode难题到项目实战
你是否还在为动态规划的复杂状态转移烦恼?是否遇到需要最优解却不知从何下手的编程问题?本文将通过gh_mirrors/leet/leetcode项目中的实际案例,用3个步骤带你掌握贪心算法(Greedy Algorithm)——这种能在每一步做出局部最优选择,最终得到全局最优解的高效思想。读完本文你将获得:识别贪心问题的3个特征、5类经典题型的解题模板、项目中151道题的贪心题解索引。
贪心算法的核心思想
贪心算法是一种在每一步选择中都采取当前状态下最优决策的算法思想。它与动态规划的主要区别在于:贪心不回溯,通过局部最优积累全局最优;动态规划则需要保存中间状态,通过子问题的解推导最终答案。
适用场景的3个特征
- 最优子结构:问题的最优解包含子问题的最优解
- 无后效性:过去的选择不会影响未来的状态
- 贪心选择性质:局部最优选择能导致全局最优解
项目中贪心算法的完整理论框架可参考贪心法章节源码,其中详细分析了8类典型问题的数学证明与实现方法。
步骤1:识别贪心问题——从"跳游戏"看决策模式
问题场景
给定非负整数数组,每个元素代表在该位置能跳的最大长度,判断能否到达最后一个位置。例如数组[2,3,1,1,4]返回true,[3,2,1,0,4]返回false。
贪心决策过程
从起点开始,每一步都选择能到达的最远距离:
- 记录当前能到达的最右位置
reach - 遍历每个位置,更新
reach为max(reach, i + nums[i]) - 若
reach大于等于数组长度,返回true
核心代码实现
// 思路1,时间复杂度O(n),空间复杂度O(1)
class Solution {
public:
bool canJump(const vector<int>& nums) {
int reach = 1; // 最右能跳到哪里
for (int i = 0; i < reach && reach < nums.size(); ++i)
reach = max(reach, i + 1 + nums[i]);
return reach >= nums.size();
}
};
该实现来自项目贪心法章节的"Jump Game"问题解析,还提供了逆向遍历和动态规划两种对比解法。
步骤2:掌握5类经典题型的贪心策略
1. 区间调度问题
策略:按结束时间排序,选择最早结束的区间
2. 资源分配问题
以"买卖股票的最佳时机II"为例,允许多次交易但不能同时持有多只股票:
策略:只要后一天价格高于前一天,就进行一次交易
// 时间复杂度O(n),空间复杂度O(1)
class Solution {
public:
int maxProfit(vector<int> &prices) {
int sum = 0;
for (int i = 1; i < prices.size(); i++) {
int diff = prices[i] - prices[i - 1];
if (diff > 0) sum += diff;
}
return sum;
}
};
完整代码及注释见Best Time to Buy and Sell Stock II。
3. 字符处理问题
"最长不含重复字符的子串"问题中,贪心策略表现为:
策略:遇到重复字符时,以上一个重复字符的下一位作为新起点
项目中提供了时间复杂度O(n)的优化实现,通过记录字符上次出现位置实现线性扫描。
4. 双指针贪心
"盛最多水的容器"问题中,使用左右指针向中间逼近:
策略:移动高度较小的指针,因为容器容量由短板决定
int maxArea(vector<int> &height) {
int start = 0, end = height.size() - 1, result = INT_MIN;
while (start < end) {
int area = min(height[end], height[start]) * (end - start);
result = max(result, area);
height[start] <= height[end] ? start++ : end--;
}
return result;
}
5. 区间覆盖问题
如"Jump Game II"要求计算到达终点的最少跳跃次数,采用分层跳跃策略:
策略:记录当前层能到达的最右边界,遍历该层所有位置更新下一层边界
详细实现及两种解法对比见Jump Game II章节。
步骤3:项目实战与题解索引
贪心算法题解目录
gh_mirrors/leet/leetcode项目包含151道LeetCode题的完整题解,其中贪心算法相关题目分布如下:
| 题型分类 | 题目数量 | 难度分布 | 题解路径 |
|---|---|---|---|
| 区间问题 | 8题 | 中等6题,困难2题 | chapGreedy.tex |
| 资源分配 | 5题 | 简单2题,中等3题 | chapGreedy.tex |
| 字符处理 | 3题 | 中等2题,困难1题 | chapString.tex |
| 双指针 | 6题 | 中等4题,困难2题 | chapLinearList.tex |
| 其他应用 | 4题 | 中等3题,困难1题 | chapGreedy.tex |
扩展学习资源
- 项目完整PDF:leetcode-cpp.pdf包含所有题解的打印版
- 算法对比分析:动态规划与贪心的适用边界在chapDynamicProgramming.tex中有详细讨论
- 练习题:README.md提供了按算法分类的题目索引
总结与实践建议
贪心算法是解决优化问题的利器,但并非万能。实际解题时建议:
- 先尝试证明贪心选择性质,可采用反证法
- 从简单案例入手,归纳最优子结构
- 与动态规划解法对比,评估时间空间复杂度
项目中151道题的题解均提供了多种算法的实现对比,建议重点研究贪心与动态规划的转换条件。掌握贪心思想不仅能提升解题效率,更能培养在复杂问题中快速找到突破口的能力。收藏本文,下次遇到优化问题时,回来对照这3个步骤,你会发现贪心算法没那么难。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考





