周末的限时自助餐厅里,小周端着托盘站在取餐台前。厨师告诉他:现在 90 分钟,托盘大小固定,拿了就不能反悔。小周没有翻菜谱、没有规划每一口,而是每次绕场一圈,挑眼前看起来最值得的一盘,拿完就继续走向下一个窗口。这种“现在看着最好就选它,不回头、不重来”的决策方式,就是贪心算法。 贪心算法的英文是 Greedy,算法世界里的“贪心侠”。它和穷举、动态规划最大的不同,是不做全局推演,只靠一条清晰规则步步递进。可问题是:凭什么“每次选当下最好”最后就能得到全局最好?如果能回答这个问题,你就在面试里占了先机。接下来的内容里,我会用小周吃自助餐的故事,把贪心算法的直觉、证明、代码框架、经典题型和面试避坑讲清楚。

岗位关联

掌握贪心算法,就等于掌握算法面试中最常见的一类“秒杀题”套路:先判断性质,再排序扫描,快速写出高效解。这是 AI 工程师算法笔试中最容易拿分的板块之一。

目录
  1. 可视化演示
  2. 贪心算法直觉:先拿最顺眼的菜
  3. 两大支柱性质:为什么这顿饭能吃饱
  4. 实现框架复杂度:先排队再打饭
  5. 区间调度实战:让会议室高效转起来
  6. 跳跃游戏实战:看得见的最远一步
  7. 贪心与动态规划:自助餐与全桌计算
  8. 面试套路与避坑:五步检票流程
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示贪心算法局部最优选择过程,活动安排经典问题求解,以及最优子结构性质的递推验证。

本节配有交互动效(进入视口后自动加载,约 3KB)。

贪心选择性质 每步选择当前最优解,不考虑全局后果 贪心≠最优 贪心策略在满足贪心选择性质时可得全局最优

贪心算法直觉:先拿最顺眼的菜

小周走进自助餐厅时,其实根本没想好 90 分钟里要吃什么。他的规则很简单:每轮绕场一圈,从眼前还没拿过的菜里,挑“当下最想吃的”那一盘。拿完就走,绝不回头,也不因后面出现了更好的菜而后悔。这就是贪心算法最核心的直觉:用当前可见的最优选择,代替对未来的全盘规划。

在算法里,这道“最顺眼的菜”通常用一个评价函数来量化。比如做区间调度时,我们评价“结束时间最早”;做跳跃游戏时,我们评价“能到达的最远位置”。每一轮都在候选集里选出这个评价最高的元素,然后更新状态,重复执行。贪心不像暴力搜索那样试遍所有路径,它只走一条不回头的主线。

这个策略听起来很“莽”,但它恰恰解决了一大批面试题。许多贪心题的第一步是排序:把候选元素按某个键排好,再从头到尾扫一遍。排序的目的,就是让“每次选当前最优”变成“当前指针指向的位置天然就是最优”。

小周之所以敢这么吃,是因为在这家餐厅,先拿哪道菜不会影响后面菜品的出现顺序,也不会占用额外资源。真实题目也一样:如果“当前拿了什么”会影响后续决策,贪心就未必成立;如果影响不大,贪心往往就是最快的答案。

at∗=arg⁡max⁡a∈Ctv(a)\displaystyle a_t^{\ast}=\arg\max_{a\in C_t} v(a)
这个公式表示:第 \(t\) 步,从还剩下的候选集合 \(C_t\) 里,选出评价函数 \(v(a)\) 最大的那个元素 \(a_t^{\ast}\)。
  1. 候选集合 \(C_t\)这是第 \(t\) 步时“还没被拿走过”的全部选择,相当于自助餐里还没取过的菜。
    CtC_t
  2. 评价函数 \(v(a)\)给每个候选元素打分。分数越高,代表“当下越想要”。
    v(a)v(a)
  3. \(\arg\max\) 操作在所有候选中找出让分数最高的那个元素,而不是把最高分本身取出来。
    arg⁡max⁡a∈Ctv(a)\arg\max_{a\in C_t} v(a)
  4. 结果 \(a_t^{\ast}\)第 \(t\) 步真正被选走的最优候选,也就是小周这一轮端走的盘子。
    at∗a_t^{\ast}

两大支柱性质:为什么这顿饭能吃饱

小周试了几次发现,光靠“当下喜欢”还不够,除非餐厅有两个隐藏机制:第一,某道菜一定出现在某种最优吃法中;第二,拿走这道菜后,剩下的问题依然是“从剩余菜里选最优”,子问题和原问题长得一模一样。在算法里,这两个机制就是贪心选择性质和最优子结构。

以“会议室安排”为例:有若干会议,每个会议有开始时间 \(a_i\) 和结束时间 \(b_i\),要选出最多互不冲突的会议。贪心策略是每次选结束时间最早的会议。为什么这样对?因为结束时间最早,就给后面的会议留下了最大的剩余空间。如果某个最优解第一个选的是会议 \(j\),而贪心选的会议 \(g\) 结束更早,那把 \(j\) 换成 \(g\) 后,后面所有会议依然可以原样安排,不会变得更差。

这种“把最优解里的元素换成贪心元素”的操作,叫做交换论证。交换后解不劣,就说明贪心选择是有资格进入某个最优解的。接着再用数学归纳法或反证法补完,就构成了完整的正确性证明。这部分面试中不一定要求手写全证明,但至少要说得出思路。

有了这两根支柱,贪心就不是“赌运气”,而是“有证明的快速算法”。面试时如果能主动说出“这题满足贪心选择性质和最优子结构”,就已经比大多数候选人高一个段位。

fg=min⁡i∈Ifi⇒∃ O∗: g∈O∗\displaystyle f_g=\min_{i\in I} f_i \quad\Rightarrow\quad \exists\, O^{*}:\ g\in O^{*}
公式意思是:如果 \(g\) 是所有候选区间里结束时间最早的,那么一定存在一个最优解 \(O^{*}\),其中包含 \(g\)。
  1. \(f_i\) 表示结束时间每个候选区间 \(i\) 都有自己的结束边界,类比每道菜的“下线时间”。
    fif_i
  2. \(\min_{i\in I} f_i\)在整个候选集合里取结束时间最小的那个区间,也就是“最早退场的菜”。
    min⁡i∈Ifi\min_{i\in I} f_i
  3. \(g\) 表示贪心选中的元素它因为结束最早而被选中,成为当前这一步的策略答案。
    gg
  4. \(\exists\, O^{}g\in O^{}\):数学上严谨地说,“存在”一个最优解包含 \(g\)。这给了贪心继续走下去的资格证。
    ∃ O∗:g∈O∗\exists\, O^{*}: g\in O^{*}

实现框架复杂度:先排队再打饭

小周后来总结出自己吃自助餐的标准动作:第一步,先绕着餐厅走一圈,把所有菜按“心动程度”从高到低排个序;第二步,从心动值最高的菜开始,一盘一盘看,能拿就拿;第三步,每拿一盘,更新一下托盘剩余空间。这就是贪心算法的三段式框架:排序、循环、更新。

大部分贪心题的实现代码都极短,往往不超过二十行。真正决定效率的,常常是排序这一步。假设候选集合大小为 \(n\),排序的代价是 \(O(n\log n)\),之后只需线性扫描一遍。更新状态的代价通常是常数或对数级。因此,贪心算法的总复杂度主要由排序决定。

面试里写代码之前,先说出复杂度,会让答案显得更完整。通常说完 \(O(n\log n)\) 之后,还要补一句“空间复杂度是 \(O(n)\),因为要存结果或临时列表”。这样面试官就知道你不只会背模板,还清楚每一部分的开销。

其实很多贪心题在排序之前毫无头绪,一旦排序键想清楚了,代码就是顺水推舟。小周也发现,提前“排队”能让他不再纠结:菜已经按心动值排好,直接照着顺序拿就行。

T(n)=O(nlog⁡n)+O(n)=O(nlog⁡n)\displaystyle T(n)=O(n\log n)+O(n)=O(n\log n)
排序需要 \(O(n\log n)\),扫描需要 \(O(n)\),两者相加后主导项仍是 \(O(n\log n)\)。
  1. 排序耗时 \(O(n\log n)\)把候选集合按排序键排列,这是贪心题里最常见的预处理。
    O(nlog⁡n)O(n\log n)
  2. 扫描耗时 \(O(n)\)排好序后,从头到尾检查每个候选是否可行,总体是线性开销。
    O(n)O(n)
  3. 总耗时 \(T(n)\)两部分相加,保留增长最快的量级,得到最终复杂度。
    T(n)=O(nlog⁡n)+O(n)T(n)=O(n\log n)+O(n)
  4. 最终化简因为 \(n\log n\) 比 \(n\) 增长更快,所以舍去线性项。
    O(nlog⁡n)O(n\log n)

区间调度实战:让会议室高效转起来

有一类面试题会让小周特别熟练:公司只有一个会议室,各部门提交了使用时间段,问最多能排几场会议。这里的候选元素是若干区间,每个区间有开始时间 \(a_i\) 和结束时间 \(b_i\),目标是选出的区间两两不重叠,并且数量最多。

贪心解法是:把所有区间按结束时间从小到大排序,然后从第一个开始扫描;只要当前区间的开始时间不小于上一个被选区间的结束时间,就选它,否则跳过。这样选出的区间数量就是最大化的。为什么不是按开始时间排序?因为一个区间结束得越早,越不会遮挡后面的会议,相当于小周在自助餐里先拿走“占时最短”的菜,给后面留出更多胃口。

这道题也是“分发饼干”“最少硬币”等经典贪心题的“母题”:先确定排序键,再确定可行性判断条件,最后更新状态。区间调度本身还会变形为“最多观看节目数”“最多完成工作数”,但解法骨架完全一致。

小周在自助餐厅也遇到过类似场景:有些菜需要排队等很久,如果先拿等待时间短的菜,反而能在同样时间里吃到更多种类。区间调度题的精髓,就是把“怎么选最多”变成“选最早结束的”,一次扫描,答案自现。

k∗=max⁡{k:∃ g1≺g2≺⋯≺gk, bgt≤agt+1}\displaystyle k^{\ast}=\max\bigl\{k:\exists\,g_1\prec g_2\prec\cdots\prec g_k,\ b_{g_t}\le a_{g_{t+1}}\bigr\}
它表示:我们要找最大的 \(k\),使得存在 \(k\) 个选中的区间,按顺序排好后,每个区间的结束时间都不超过下一个区间的开始时间。
  1. \(k^{\ast}\) 表示最优数量最终能安排下的最大会议数。
    k∗k^{\ast}
  2. 选中的区间序列 \(g_1\prec g_2\prec\cdots\prec g_k\)这些区间按某种顺序排列成一条链,对应小周按顺序一连串取菜。
    g1≺g2≺⋯≺gkg_1\prec g_2\prec\cdots\prec g_k
  3. 不重叠条件 \(b_{g_t}\le a_{g_{t+1}}\)前一个区间的结束时间不能晚于下一个区间的开始时间,会议才能接得上。
    bgt≤agt+1b_{g_t}\le a_{g_{t+1}}
  4. 最大化 \(k\)在所有可行链中挑出最长的一条,这就是贪心最终的目标。
    max⁡{k}\max\{k\}

跳跃游戏实战:看得见的最远一步

小周吃完饭,在公园里玩“跳石头”游戏。每块石头上写着一个数字,表示“从这里最多还能往前跳多远”,他想知道能不能跳到对岸。最笨的办法是枚举每一种跳法,但小周只用记住一件事:到目前为止,能跳到的最远石头是哪一个。

贪心解法也很简单:从左往右遍历每一块石头,不断更新“最远可达位置”。只要当前位置不超过最远可达位置,就说明当前石头是够得到的;如果遍历过程中最远可达位置已经覆盖了终点,就说明可以跳到。这个思路在“最少跳跃次数”的变体中同样有效:每次走到当前覆盖范围的边界时,跳跃次数加一,同时把边界更新成当前能到达的最远位置。

这个问题的绝妙之处在于,它不需要记录具体路径,只需要一个变量持续滚动更新。很多看起来需要回溯的题,其实都可以用这种“边界+最远可达”的贪心框架解决。

小周的移动方式看似走一步看一步,但他其实一直在计算“最远能到哪儿”。面试里遇到跳跃游戏,先想清楚边界变量,再写循环,代码很快就能落地,而且不容易漏掉边界条件。

ri=max⁡(ri−1, i+nums[i])\displaystyle r_i=\max\bigl(r_{i-1},\ i+\mathrm{nums}[i]\bigr)
公式表示遍历到第 \(i\) 个位置时,最远可达位置等于“之前的最远位置”和“从当前位置能跳到的最远位置”两者中的较大值。
  1. \(r_{i-1}\) 表示历史最远遍历到 \(i\) 之前,已经通过其他石头达到的最远位置。
    ri−1r_{i-1}
  2. 当前位置 \(i\)现在正踩着的这块石头,是下标位置。
    ii
  3. 跳跃能力 \(\mathrm{nums}[i]\)当前石头上写的数字,代表最多还能跳多远。
    nums[i]\mathrm{nums}[i]
  4. 更新公式 \(\max(r_{i-1}, i+\mathrm{nums}[i])\)把历史记录和当前可能合在一起,取更远的那一个。
    max⁡(ri−1, i+nums[i])\max\bigl(r_{i-1},\ i+\mathrm{nums}[i]\bigr)

贪心与动态规划:自助餐与全桌计算

如果自助餐规则变成“拿了一道菜,就会改变其他菜的味道”,小周的“不回头”策略就可能失败。这时他需要另一种思路:像服务员一样,把菜单上所有搭配都算一遍,再挑最好的一组。这就是动态规划。动态规划用一张表记录所有子问题的答案,虽然慢,但一定安全。

贪心与动态规划最典型的对比是 0-1 背包问题:有若干物品,每个有重量 \(w_i\) 和价值 \(v_i\),背包容量为 \(W\),问最多能带走多少价值。按“单位重量价值”做贪心在有些用例上会失败,因为物品不可分割,选了一个大而轻的物品,可能挤掉两个小而重的组合。动态规划则通过填表枚举每一种容量下的最优组合。

面试时,如果一时分不清该用哪个,可以先试探贪心:构造一个反例,看它会不会翻车。找不到反例,再试着用交换论证说明贪心成立;如果明显存在反例,就果断转动态规划。贪心像小周凭感觉快速吃一轮,DP 像后厨把所有菜谱都算一遍。

贪心并不是动态规划的劣化版,而是“快速通道”。一旦题目确实满足贪心性质,它的实现和时空开销都远优于 DP。小周把这两种方法放在餐厅场景里理解:贪心是边走边选,DP 是先把整个菜单翻开算一遍;面试官更希望你先判断清楚该走哪条路。

dp[i][w]=max⁡(dp[i−1][w], dp[i−1][w−wi]+vi)\displaystyle dp[i][w]=\max\bigl(dp[i-1][w],\ dp[i-1][w-w_i]+v_i\bigr)
这是 0-1 背包的动态规划状态转移方程:面对第 \(i\) 个物品,要么不拿,要么在腾出重量 \(w_i\) 后把它拿上。
  1. 状态 \(dp[i][w]\)只考虑前 \(i\) 个物品、背包容量为 \(w\) 时的最大价值。
    dp[i][w]dp[i][w]
  2. 不拿当前物品第 \(i\) 个物品直接跳过,价值继承上一行的结果。
    dp[i−1][w]dp[i-1][w]
  3. 拿当前物品先减去它的重量 \(w_i\),再加上它的价值 \(v_i\)。
    dp[i−1][w−wi]+vidp[i-1][w-w_i]+v_i
  4. 取最大值在“不拿”和“拿”两个选项里选更好的,这就是子问题最优的积累。
    max⁡(dp[i−1][w],dp[i−1][w−wi]+vi)\max(dp[i-1][w], dp[i-1][w-w_i]+v_i)

面试套路与避坑:五步检票流程

小周在模拟面试中总结出一套“过检票口”的流程:看到题目,先不急着写代码,而是问自己三个问题:候选集合是什么?排序键是什么?可行性判断条件是什么?这三个问题想明白,贪心题就解决了一半。

第二步是构造反例。拿硬币面值 \(\{1,3,4\}\) 凑 6 元举例:如果每次优先拿面值最大的,贪心会拿到 4、1、1 共三枚;但最优解是 3 和 3 两枚。这说明贪心的排序键不是万能的。面试中,一个反例就能让你避免写出注定错误的代码。第三步是检查边界,比如列表为空、只有一个元素、所有区间完全重叠、金额为 0 等情况。最后再分析时间复杂度和空间复杂度,给面试官一个完整方案。

小周把反例当作“验票员”:通过验票,贪心方案才允许进入下一轮;通不过,就换用动态规划或其他方法。面试中最大的加分项,往往不是你一次写对,而是你能主动说出“这里我考虑过反例,所以选择更稳妥的解法”。

4+1+1=3+3=6,4+1+1=3 coins, 3+3=2 coins\displaystyle 4+1+1=3+3=6,\qquad 4+1+1=3\text{ coins},\ 3+3=2\text{ coins}
同样是 6 元,贪心策略需要 3 枚硬币,而合理分组只需要 2 枚,这就是贪心失效的经典反例。
  1. 目标金额 6我们的任务是用给定面值凑出 6 元。
    66
  2. 贪心选择的 \(4+1+1\)每次都拿不超过剩余金额的最大面值,最终是 3 枚硬币。
    4+1+1=3 coins4+1+1=3\text{ coins}
  3. 最优选择的 \(3+3\)绕过贪心直觉,直接用两枚 3 元硬币凑出目标。
    3+3=2 coins3+3=2\text{ coins}
  4. 对比两个方案两边金额相等,币数却不同,说明贪心在此题不成立。
    3>23>2

AI 应用场景

GPU 训练任务调度

阶段:训练资源管理与调度

解决的问题:多个深度学习训练任务同时向集群申请 GPU,传统“先到先得”会导致短任务被长任务堵住,GPU 窗口利用率低,单位时间完成的任务数少。用贪心区间调度思路,把每个任务看作一个时间段,按“预期结束时间”排序,优先安排能最早释放资源的任务,可显著提高任务完成数量。

具体案例:某 MLOps 平台每天有几十个模型微调任务排队。调度器只保留“当前最可能及时完成的候选任务”,让短任务先跑,长任务顺延,最终线上 GPU 利用率提升了约 20%。

特征选择的贪心前向搜索

阶段:特征工程

解决的问题:高维数据下特征数量过多,模型训练慢且容易过拟合。全量特征组合搜索会爆炸,贪心前向搜索每次只选一个“当前对验证集提升最大”的特征加入集合,选完不再回头,快速得到一个可用的特征子集。

具体案例:广告点击率预估项目中,原始特征有 2000 维。算法工程师用贪心筛选逐步选出 120 维核心特征,模型 AUC 提高了约 3%,训练时间缩短了一半。

在线推理预算分配

阶段:在线服务与资源优化

解决的问题:大模型推理服务有预算约束,无法为所有请求都分配最长生成时间。按“生成收益/计算成本”的性价比排序,贪心地把预算优先分配给高价值请求,可以在有限资源下获得更高总收益。

具体案例:某智能客服系统为不同用户问题分配 token 预算,优先保证高价值问题得到最长回答,低价值问题快速收敛,整体用户满意度保持稳定的同时,推理成本下降了约 30%。

Python 演示

python44 行
def max_non_overlap(intervals):    intervals.sort(key=lambda x: x[1])    picked = []    last_end = float('-inf')    for start, end in intervals:        if start >= last_end:            picked.append((start, end))            last_end = end    return picked  def min_jumps(nums):    n = len(nums)    if n <= 1:        return 0    jumps = 0    cur_end = 0    farthest = 0    for i in range(n - 1):        farthest = max(farthest, i + nums[i])        if i == cur_end:            jumps += 1            cur_end = farthest            if cur_end >= n - 1:                break    return jumps  def greedy_coin_count(coins, amount):    coins.sort(reverse=True)    count = 0    rest = amount    for coin in coins:        while rest >= coin:            rest -= coin            count += 1    return count if rest == 0 else -1  if __name__ == '__main__':    meetings = [(1, 3), (2, 5), (4, 6), (5, 8), (7, 9)]    print(max_non_overlap(meetings))    print(min_jumps([2, 3, 1, 1, 4]))    print(greedy_coin_count([1, 3, 4], 6))
目的:用三个函数分别演示贪心在区间调度、跳跃游戏和硬币找零上的表现,最后一个函数专门用来暴露贪心失效的反例。
  1. intervals.sort(key=lambda x: x[1])这是区间调度的关键:按结束时间从小到大排序,让贪心选择天然可见。
  2. if start >= last_end:判断当前会议会不会和上一个被选会议冲突,不冲突才收下。
  3. picked.append((start, end))把选中的区间加入结果列表,同时后续更新 last_end。
  4. farthest = max(farthest, i + nums[i])跳跃游戏的核心更新:不断刷新最远可达位置。
  5. if i == cur_end:走到当前边界时,必须再跳一次,同时把边界更新成 farthest。
  6. coins.sort(reverse=True)硬币找零的贪心排序:面值从大到小尝试。
  7. while rest >= coin:只要还能用当前面值,就尽量多拿,这是典型的“当下最优”动作。
  8. return count if rest == 0 else -1如果剩余金额不为 0,说明贪心在这个面值组合下失败。

常见误区

误区贪心算法不就是每次选最大、最贵、最长的那个吗?我在面试里看到“选最多”就按最大价值排序,结果经常出错。

正解贪心的核心是“选当前最优”,但“最优”的标准因题而异。区间调度里,最优不是选最长的区间,而是选结束时间最早的区间;跳跃游戏里,最优不是跳得远就无脑跳,而是维护最远可达。排序键才是贪心的灵魂,选错了键,后面的步骤再规范也没用。

为何常见:生活里“贪心”一词会让人联想到“拿最多的好东西”,于是初学者本能地按最大值、最大面值排序,忽略了题目真正的约束条件。

误区只要每一步都选当前最好的,最后结果就一定是最好的。所以我做贪心题从来不需要考虑反例。

正解局部最优形成全局最优,必须满足贪心选择性质和最优子结构。硬币面值 \(\{1,3,4\}\) 凑 6 就是一个反例:贪心选 4 后再选两个 1,共 3 枚;最优解是两个 3,共 2 枚。不能证明性质时,必须用反例验证。

为何常见:教材里现成的贪心经典题,比如哈夫曼编码、最小生成树,都已经验证过正确性,学习者容易产生“所有贪心都对”的错觉。

误区代码能跑通用例就行,面试里直接写答案,没必要讲什么是交换论证、数学归纳和反证法。

正解面试官考察的不仅是代码,更是决策依据。如果题目本身贪心不成立,你跑过几个用例也救不回来。真正专业的回答是:先说“这题我尝试用贪心”,再讲清楚排序依据,再用交换论证说明贪心元素可以进入某个最优解,最后分析复杂度。

为何常见:在线评测系统只判断对错,不判断推理过程,导致很多人习惯了“跑通万岁”的工程思维。

误区动态规划更高级、更安全,贪心只是 DP 的偷懒版,能不用就不用。

正解两者是并列的算法思想。贪心一旦成立,往往比 DP 快一个量级,而且代码更简洁;DP 是全局可靠的兜底方案,但时间和空间代价更高。面试时先判断贪心,再用 DP 兜底,才是高效的解题策略。

为何常见:动态规划听起来更“硬核”,很多学习者把算法按印象分排序,低估了贪心的理论价值和面试价值。

面试题

Q1. 贪心算法的核心思想是什么?

单选题 · easy

  • A.每次选择当前看起来最优的解← 正确答案
  • B.先枚举所有可能的全局解
  • C.随机选择之后逐步修正
  • D.先求出所有子问题最优解

答案:A。贪心强调“当前最优、不回头”,不做全局枚举,也不回溯修正。B 是暴力搜索,C 是随机优化,D 是动态规划的思路。

Q2. 区间调度通常按哪个顺序排序后贪心?

单选题 · easy

  • A.按区间左端点从大到小
  • B.按区间右端点从小到大← 正确答案
  • C.按区间长度从短到长
  • D.按区间中点从近到远

答案:B。按右端点从小到大排序,才能让每次选择为后续留出最大空间。这就像自助餐里先拿“最早结束”的菜,才能腾出更多时间吃别的。

Q3. 硬币面值 {1,3,4},凑 6 元贪心为什么会失败?

单选题 · mid

  • A.贪心先取 4 导致最终多一枚← 正确答案
  • B.贪心无法处理零钱找零
  • C.1 元面值没有被排序
  • D.金额必须是 2 的倍数

答案:A。贪心先选面值 4,剩余 2 只能用两枚 1 元,共 3 枚;而最优解是 3+3,只需 2 枚。问题出在贪心选择面值 4 后破坏了后续的凑币能力,说明局部最优没有通向全局最优。

Q4. 跳跃游戏中贪心主要维护什么?

单选题 · mid

  • A.当前能到达的最远位置← 正确答案
  • B.当前走过的所有步数
  • C.每次起跳的最短距离
  • D.全部元素的总和值

答案:A。跳跃游戏只需维护“最远可达位置”,不断用当前下标加上跳跃力去更新它。一旦最远位置覆盖终点,就说明可达,这正是贪心“只记边界”的体现。

Q5. 关于最优子结构,下列说法正确的是?

单选题 · mid

  • A.全局最优包含子问题的最优← 正确答案
  • B.所有子问题都可以直接忽略
  • C.贪心的选择结果一定唯一
  • D.不需要对结构做任何验证

答案:A。最优子结构要求全局最优可以由子问题最优组合而成。如果子问题不可独立求解,贪心就无法递推。贪心选择不一定唯一,子问题更不能忽略,所以 A 正确。

Q6. 下列哪些常用于证明贪心正确?

多选题 · mid

  • A.交换论证法← 正确答案
  • B.数学归纳法← 正确答案
  • C.反证法← 正确答案
  • D.蒙特卡洛模拟

答案:ABC。交换论证把最优解中的元素替换成贪心元素,证明不会变差;数学归纳法把贪心步骤逐层推进;反证法假设贪心不是最优,再推出矛盾。蒙特卡洛模拟只能抽样验证,不能给出严格证明。

Q7. 关于贪心与动态规划,以下说法正确的是?

多选题 · mid

  • A.贪心速度快但不保证正确← 正确答案
  • B.动态规划可以保证得到最优← 正确答案
  • C.贪心正确时通常效率更高← 正确答案
  • D.贪心一定比动态规划差

答案:ABC。贪心时间复杂度通常很低,但必须满足性质才正确;DP 通过状态表枚举所有可能,通常能得到全局最优。贪心和 DP 是两种工具,不存在“一定差”的关系。

Q8. 区间调度用贪心相比动态规划的主要优势是什么?

单选题 · hard

  • A.时间可降到 O(n log n) 量级← 正确答案
  • B.时间仍为 O(n^3) 量级
  • C.时间会降到 O(2^n) 量级
  • D.时间会是 O(n!) 量级

答案:A。贪心先排序再扫描,时间复杂度为 \(O(n\log n)\)。而动态规划在区间问题上通常需要排序后做二阶甚至更高阶枚举,复杂度更高。这就是“正确贪心远快于 DP”的体现。

Q9. 面试中使用贪心解法的完整步骤包括?

多选题 · mid

  • A.确定排序依据← 正确答案
  • B.尝试构造反例← 正确答案
  • C.跳过任何证明
  • D.检查边界复杂度← 正确答案

答案:ABD。靠谱的贪心流程是先确定排序键,再构造反例验证,随后分析边界和复杂度。跳过证明和反例,直接写代码是面试中最容易翻车的做法。

Q10. 贪心选择性质和最优子结构同时满足时可以怎么判断?

单选题 · hard

  • A.贪心基本正确且常是最优← 正确答案
  • B.只需要满足其中一个
  • C.最优子结构可以忽略
  • D.与排序依据无关

答案:A。两个性质同时满足,贪心就具备了完整的理论支撑,通常可以得到最优解。只满足其中一个仍可能失败;最优子结构不可忽略;排序依据反而决定了贪心选择性质是否成立。