Skip to content

贪心

本章目标:理解贪心选择性质,掌握跳跃游戏、区间调度等经典贪心模型。

你将学到

  • 贪心选择性质的含义
  • 跳跃游戏模型
  • 区间调度与区间合并
  • 分发糖果模型
  • 什么时候贪心不成立
  • 贪心与 DP 的区别

1. 概念

贪心算法在每一步选择中都采取当前状态下最优的选择,期望最终得到全局最优解。贪心成立的条件是贪心选择性质:局部最优能推导出全局最优。

贪心 vs 动态规划:

  • DP:穷举所有状态,保证最优,但通常 O(n²) 以上
  • 贪心:只选当前最优,O(n log n) 或 O(n),但不保证正确

验证贪心策略是否正确的常用方法:反证法——假设存在更优解与贪心选择矛盾,推出矛盾。

2. 核心模板

区间调度(选最多不重叠区间)

python
def eraseOverlapIntervals(intervals):
    """移除最少区间使剩余不重叠 = 选最多不重叠区间"""
    if not intervals:
        return 0
    intervals.sort(key=lambda x: x[1])  # 按右端点排序
    count = 1
    end = intervals[0][1]
    for i in range(1, len(intervals)):
        if intervals[i][0] >= end:  # 不重叠
            count += 1
            end = intervals[i][1]
    return len(intervals) - count

跳跃游戏(贪心)

python
def canJump(nums):
    """能否到达最后一个位置"""
    max_reach = 0
    for i in range(len(nums)):
        if i > max_reach:
            return False  # 当前位置不可达
        max_reach = max(max_reach, i + nums[i])
        if max_reach >= len(nums) - 1:
            return True
    return True

复杂度

  • 区间调度:排序 O(n log n) + 遍历 O(n)
  • 跳跃游戏:O(n)

3. 经典题目精讲

LeetCode 45 · 跳跃游戏 II

题意:给定非负整数数组,每个元素代表最大跳跃步数,求到达末尾的最少跳跃次数。

思路:贪心——在当前跳跃范围内选下一步能到达最远的位置。

python
def jump(nums):
    if len(nums) <= 1:
        return 0
    jumps = 0
    cur_end = 0  # 当前跳跃的边界
    max_reach = 0  # 下一步能到的最远位置
    for i in range(len(nums) - 1):
        max_reach = max(max_reach, i + nums[i])
        if i == cur_end:  # 到达边界,必须跳
            jumps += 1
            cur_end = max_reach
            if cur_end >= len(nums) - 1:
                break
    return jumps

LeetCode 135 · 分发糖果

题意:N 个孩子站成一排,每个孩子有评分。给每个孩子发糖果,要求:评分高的孩子比相邻的孩子糖果多。求最少糖果数。

思路:两次遍历——从左到右保证右边评分高的比左边多,从右到左保证左边评分高的比右边多。

python
def candy(ratings):
    n = len(ratings)
    candies = [1] * n
    # 从左到右
    for i in range(1, n):
        if ratings[i] > ratings[i - 1]:
            candies[i] = candies[i - 1] + 1
    # 从右到左
    for i in range(n - 2, -1, -1):
        if ratings[i] > ratings[i + 1]:
            candies[i] = max(candies[i], candies[i + 1] + 1)
    return sum(candies)

LeetCode 452 · 用最少数量的箭引爆气球

题意:给定气球区间,一支箭可以引爆一列上所有重叠的气球,求最少箭数。

思路:按右端点排序,贪心地在最右端射箭。

python
def findMinArrowShots(points):
    if not points:
        return 0
    points.sort(key=lambda x: x[1])
    arrows = 1
    end = points[0][1]
    for s, e in points[1:]:
        if s > end:  # 当前气球不被上一支箭覆盖
            arrows += 1
            end = e
    return arrows

4. 易错点

  • ⚠️ 贪心策略不一定是正确的——如 0-1 背包问题贪心(按性价比)不成立
  • ⚠️ 区间问题注意排序依据:求最多不重叠按右端点,合并区间按左端点
  • ⚠️ 分发糖果必须两次遍历,单次会遗漏一个方向的约束

5. 推荐刷题清单

题号题名难度关键
55跳跃游戏Medium贪心可达性
45跳跃游戏 IIMedium贪心最少步数
135分发糖果Hard双向贪心
435无重叠区间Medium区间调度
452用最少箭引爆气球Medium区间贪心
621任务调度器Medium贪心+模拟

延伸阅读

基于 VitePress 构建 · 部署于 Cloudflare Pages