算法:贪心算法 - 从直觉到证明

贪心算法:从直觉到证明

开篇

面试官给了一道"区间调度"------N 个会议,时间有重叠,选最多的互不冲突会议。你先想到 DP,定义状态、写转移方程、O(n²) 搞定。面试官点头,追一句:"能更简单吗?"

你灵光一闪:按结束时间排序,每次贪心选最早结束的不冲突会议。O(n log n),代码不到 10 行。面试官满意,但紧接着一个追问:"你怎么证明贪心是对的?"

你愣了。

然后他又出了一道"零钱兑换"------面额 1, 3, 4,凑 6 元。你继续贪心:先选 4,再选 1+1,共 3 枚。他摇头:"最优是 3+3=2 枚。"

两道题都"看起来像贪心"。一道对,一道错。区别到底在哪?

这就是贪心最让人困惑的地方------它不像 DP 那样有固定的建模流程(定义状态 → 写转移 → 填表),也不像回溯那样有通用的框架(DFS + 剪枝)。贪心更像一种直觉:感觉"每步选最好的"就行。但直觉有时对,有时错,你没有一个判据。

本文要做的事情:把贪心从"靠直觉"变成"靠判据"

"贪心是 DP 的特殊情况"。本文展开这句话------什么时候你能确信"这就是那个特殊情况"?以及面试中如何用 30 秒说服面试官你的贪心策略没有 bug?

维度 说明
整体难度 白银+黄金
适合人群 会写 DP 但搞不清什么时候能用贪心代替 DP 的人
预估阅读 15 分钟
本文重点 贪心的数学本质、区间四件套的统一辨别框架、交换论证的面试表达

#mermaid-svg-rQtqfdlwz4E9G6Gd{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-rQtqfdlwz4E9G6Gd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-rQtqfdlwz4E9G6Gd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-rQtqfdlwz4E9G6Gd .error-icon{fill:#552222;}#mermaid-svg-rQtqfdlwz4E9G6Gd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-rQtqfdlwz4E9G6Gd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-rQtqfdlwz4E9G6Gd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-rQtqfdlwz4E9G6Gd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-rQtqfdlwz4E9G6Gd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-rQtqfdlwz4E9G6Gd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-rQtqfdlwz4E9G6Gd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-rQtqfdlwz4E9G6Gd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-rQtqfdlwz4E9G6Gd .marker.cross{stroke:#333333;}#mermaid-svg-rQtqfdlwz4E9G6Gd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-rQtqfdlwz4E9G6Gd p{margin:0;}#mermaid-svg-rQtqfdlwz4E9G6Gd .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-rQtqfdlwz4E9G6Gd .cluster-label text{fill:#333;}#mermaid-svg-rQtqfdlwz4E9G6Gd .cluster-label span{color:#333;}#mermaid-svg-rQtqfdlwz4E9G6Gd .cluster-label span p{background-color:transparent;}#mermaid-svg-rQtqfdlwz4E9G6Gd .label text,#mermaid-svg-rQtqfdlwz4E9G6Gd span{fill:#333;color:#333;}#mermaid-svg-rQtqfdlwz4E9G6Gd .node rect,#mermaid-svg-rQtqfdlwz4E9G6Gd .node circle,#mermaid-svg-rQtqfdlwz4E9G6Gd .node ellipse,#mermaid-svg-rQtqfdlwz4E9G6Gd .node polygon,#mermaid-svg-rQtqfdlwz4E9G6Gd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-rQtqfdlwz4E9G6Gd .rough-node .label text,#mermaid-svg-rQtqfdlwz4E9G6Gd .node .label text,#mermaid-svg-rQtqfdlwz4E9G6Gd .image-shape .label,#mermaid-svg-rQtqfdlwz4E9G6Gd .icon-shape .label{text-anchor:middle;}#mermaid-svg-rQtqfdlwz4E9G6Gd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-rQtqfdlwz4E9G6Gd .rough-node .label,#mermaid-svg-rQtqfdlwz4E9G6Gd .node .label,#mermaid-svg-rQtqfdlwz4E9G6Gd .image-shape .label,#mermaid-svg-rQtqfdlwz4E9G6Gd .icon-shape .label{text-align:center;}#mermaid-svg-rQtqfdlwz4E9G6Gd .node.clickable{cursor:pointer;}#mermaid-svg-rQtqfdlwz4E9G6Gd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-rQtqfdlwz4E9G6Gd .arrowheadPath{fill:#333333;}#mermaid-svg-rQtqfdlwz4E9G6Gd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-rQtqfdlwz4E9G6Gd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-rQtqfdlwz4E9G6Gd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rQtqfdlwz4E9G6Gd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-rQtqfdlwz4E9G6Gd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rQtqfdlwz4E9G6Gd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-rQtqfdlwz4E9G6Gd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-rQtqfdlwz4E9G6Gd .cluster text{fill:#333;}#mermaid-svg-rQtqfdlwz4E9G6Gd .cluster span{color:#333;}#mermaid-svg-rQtqfdlwz4E9G6Gd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-rQtqfdlwz4E9G6Gd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-rQtqfdlwz4E9G6Gd rect.text{fill:none;stroke-width:0;}#mermaid-svg-rQtqfdlwz4E9G6Gd .icon-shape,#mermaid-svg-rQtqfdlwz4E9G6Gd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rQtqfdlwz4E9G6Gd .icon-shape p,#mermaid-svg-rQtqfdlwz4E9G6Gd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-rQtqfdlwz4E9G6Gd .icon-shape .label rect,#mermaid-svg-rQtqfdlwz4E9G6Gd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rQtqfdlwz4E9G6Gd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-rQtqfdlwz4E9G6Gd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-rQtqfdlwz4E9G6Gd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 贪心算法
本质: DP 的降维
成立的数学条件
证明方法
区间问题: 四个问法
深度案例: 股票买卖
面试实战
DP: 枚举所有决策 O(SxD)
贪心: 只取一个决策 O(Sx1)
省掉枚举 = 节省的复杂度
贪心选择性质

存在包含贪心选择的最优解
最优子结构

子问题 + 贪心 = 全局最优
交换论证 Exchange Argument
最大不相交
最少穿刺点数
最小区间分组
最少区间覆盖

一、贪心的本质:DP 的降维打击

1.1 从 DP 的视角理解贪心

DP 的核心公式:

text 复制代码
dp[i] = 最优 { 所有决策 k:dp[子状态(k)] + 代价(k) }

这里有两层结构:外层遍历状态 i,内层枚举决策 k。DP 的时间复杂度 = 状态数 × 每个状态的决策数。

现在假设你发现了一件事:在所有合法的决策 k 中,某一个特定的 k* 永远不会比其他 k 差。 不管当前状态是什么,k* 总是最优(或并列最优)的选择。

那内层枚举就多余了------直接取 k*,不用看别的。

枚举消失了。DP 退化为贪心。复杂度降掉一个量级。

这就是贪心的本质:它不是独立于 DP 的"另一种算法",而是 DP 在特定条件下的退化形态。DP 说"我枚举所有决策取最优";贪心说"我能证明某个决策永远是最优的,所以直接取它,省去枚举"。
#mermaid-svg-7U3QgQM2i8Fnok74{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7U3QgQM2i8Fnok74 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7U3QgQM2i8Fnok74 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7U3QgQM2i8Fnok74 .error-icon{fill:#552222;}#mermaid-svg-7U3QgQM2i8Fnok74 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7U3QgQM2i8Fnok74 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7U3QgQM2i8Fnok74 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7U3QgQM2i8Fnok74 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7U3QgQM2i8Fnok74 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7U3QgQM2i8Fnok74 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7U3QgQM2i8Fnok74 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7U3QgQM2i8Fnok74 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7U3QgQM2i8Fnok74 .marker.cross{stroke:#333333;}#mermaid-svg-7U3QgQM2i8Fnok74 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7U3QgQM2i8Fnok74 p{margin:0;}#mermaid-svg-7U3QgQM2i8Fnok74 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7U3QgQM2i8Fnok74 .cluster-label text{fill:#333;}#mermaid-svg-7U3QgQM2i8Fnok74 .cluster-label span{color:#333;}#mermaid-svg-7U3QgQM2i8Fnok74 .cluster-label span p{background-color:transparent;}#mermaid-svg-7U3QgQM2i8Fnok74 .label text,#mermaid-svg-7U3QgQM2i8Fnok74 span{fill:#333;color:#333;}#mermaid-svg-7U3QgQM2i8Fnok74 .node rect,#mermaid-svg-7U3QgQM2i8Fnok74 .node circle,#mermaid-svg-7U3QgQM2i8Fnok74 .node ellipse,#mermaid-svg-7U3QgQM2i8Fnok74 .node polygon,#mermaid-svg-7U3QgQM2i8Fnok74 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7U3QgQM2i8Fnok74 .rough-node .label text,#mermaid-svg-7U3QgQM2i8Fnok74 .node .label text,#mermaid-svg-7U3QgQM2i8Fnok74 .image-shape .label,#mermaid-svg-7U3QgQM2i8Fnok74 .icon-shape .label{text-anchor:middle;}#mermaid-svg-7U3QgQM2i8Fnok74 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7U3QgQM2i8Fnok74 .rough-node .label,#mermaid-svg-7U3QgQM2i8Fnok74 .node .label,#mermaid-svg-7U3QgQM2i8Fnok74 .image-shape .label,#mermaid-svg-7U3QgQM2i8Fnok74 .icon-shape .label{text-align:center;}#mermaid-svg-7U3QgQM2i8Fnok74 .node.clickable{cursor:pointer;}#mermaid-svg-7U3QgQM2i8Fnok74 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7U3QgQM2i8Fnok74 .arrowheadPath{fill:#333333;}#mermaid-svg-7U3QgQM2i8Fnok74 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7U3QgQM2i8Fnok74 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7U3QgQM2i8Fnok74 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7U3QgQM2i8Fnok74 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7U3QgQM2i8Fnok74 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7U3QgQM2i8Fnok74 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7U3QgQM2i8Fnok74 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7U3QgQM2i8Fnok74 .cluster text{fill:#333;}#mermaid-svg-7U3QgQM2i8Fnok74 .cluster span{color:#333;}#mermaid-svg-7U3QgQM2i8Fnok74 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7U3QgQM2i8Fnok74 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7U3QgQM2i8Fnok74 rect.text{fill:none;stroke-width:0;}#mermaid-svg-7U3QgQM2i8Fnok74 .icon-shape,#mermaid-svg-7U3QgQM2i8Fnok74 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7U3QgQM2i8Fnok74 .icon-shape p,#mermaid-svg-7U3QgQM2i8Fnok74 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7U3QgQM2i8Fnok74 .icon-shape .label rect,#mermaid-svg-7U3QgQM2i8Fnok74 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7U3QgQM2i8Fnok74 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7U3QgQM2i8Fnok74 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7U3QgQM2i8Fnok74 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 贪心架构
DP 架构
证明 k* 永远最优

贪心选择性质成立
状态 i
枚举决策 k1, k2, k3...
取 max/min
O(状态数 × 决策数)
状态 i
直接取 k*
省去枚举
O(状态数 × 1)

打个比方:DP 像你面对菜单上 20 道菜逐一比较营养价值再点餐;贪心像你走进餐厅直接说"我每次只点当日推荐"------前提是你能证明当日推荐永远不会比其他菜差。如果这个前提不成立(比如今天推荐的恰好是你过敏的),贪心就翻车了。

1.2 贪心成立的两个数学条件

什么时候可以确信"k* 永远不比别的差"?需要两个条件同时满足:

条件一:贪心选择性质

存在一个全局最优解,包含当前步骤的贪心选择。

白话翻译:做了贪心选择之后,你不会错过全局最优解------全局最优解的某个版本里,一定包含你这步选的东西。

这是最关键的条件。DP 和贪心的唯一分界线就在这里。DP 不需要这个性质(它枚举所有决策所以不会错过),贪心必须有它(因为它只看一个决策,如果这个决策把最优解排斥了,就错了)。

条件二:最优子结构

做完贪心选择后,剩余子问题的最优解加上这个贪心选择,就是原问题的最优解。

这个条件 DP 和贪心都需要。它保证你可以"分步求解"------先做一步选择,再递归解决剩余子问题,两者拼起来就是全局解。

1.3 贪心失败的本质原因

贪心选择性质不成立时,会发生什么?

回到零钱兑换 1, 3, 4 凑 6 的例子。贪心选择 = "选面额最大的",第一步选 4。但全局最优解是 3+3,里面根本没有 4。贪心选择把全局最优解排斥了------选了 4 之后,你只能凑剩余的 2,而 2 只能用 1+1,共 3 枚。

问题出在哪?选了 4 消耗了 4 单位容量,这个消耗限制了未来的选择空间。如果你选 3,只消耗 3 单位,未来还能恰好再选一个 3。当前选择和未来选择之间有耦合------这就是贪心失败的本质。

三种典型的结构性失败:

失败模式 含义 经典例子
决策耦合 当前选择消耗了未来的资源/空间 0-1 背包、零钱兑换
需要"等一等" 当前不选反而后面收益更大 股票买卖 K 次限制
多维不可比 候选之间没有清晰的全序关系 同时有重量和价值两个维度

#mermaid-svg-gIuHQQbHIRuzo7wM{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-gIuHQQbHIRuzo7wM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gIuHQQbHIRuzo7wM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gIuHQQbHIRuzo7wM .error-icon{fill:#552222;}#mermaid-svg-gIuHQQbHIRuzo7wM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gIuHQQbHIRuzo7wM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gIuHQQbHIRuzo7wM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gIuHQQbHIRuzo7wM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gIuHQQbHIRuzo7wM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gIuHQQbHIRuzo7wM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gIuHQQbHIRuzo7wM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gIuHQQbHIRuzo7wM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gIuHQQbHIRuzo7wM .marker.cross{stroke:#333333;}#mermaid-svg-gIuHQQbHIRuzo7wM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gIuHQQbHIRuzo7wM p{margin:0;}#mermaid-svg-gIuHQQbHIRuzo7wM .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-gIuHQQbHIRuzo7wM .cluster-label text{fill:#333;}#mermaid-svg-gIuHQQbHIRuzo7wM .cluster-label span{color:#333;}#mermaid-svg-gIuHQQbHIRuzo7wM .cluster-label span p{background-color:transparent;}#mermaid-svg-gIuHQQbHIRuzo7wM .label text,#mermaid-svg-gIuHQQbHIRuzo7wM span{fill:#333;color:#333;}#mermaid-svg-gIuHQQbHIRuzo7wM .node rect,#mermaid-svg-gIuHQQbHIRuzo7wM .node circle,#mermaid-svg-gIuHQQbHIRuzo7wM .node ellipse,#mermaid-svg-gIuHQQbHIRuzo7wM .node polygon,#mermaid-svg-gIuHQQbHIRuzo7wM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gIuHQQbHIRuzo7wM .rough-node .label text,#mermaid-svg-gIuHQQbHIRuzo7wM .node .label text,#mermaid-svg-gIuHQQbHIRuzo7wM .image-shape .label,#mermaid-svg-gIuHQQbHIRuzo7wM .icon-shape .label{text-anchor:middle;}#mermaid-svg-gIuHQQbHIRuzo7wM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-gIuHQQbHIRuzo7wM .rough-node .label,#mermaid-svg-gIuHQQbHIRuzo7wM .node .label,#mermaid-svg-gIuHQQbHIRuzo7wM .image-shape .label,#mermaid-svg-gIuHQQbHIRuzo7wM .icon-shape .label{text-align:center;}#mermaid-svg-gIuHQQbHIRuzo7wM .node.clickable{cursor:pointer;}#mermaid-svg-gIuHQQbHIRuzo7wM .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-gIuHQQbHIRuzo7wM .arrowheadPath{fill:#333333;}#mermaid-svg-gIuHQQbHIRuzo7wM .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-gIuHQQbHIRuzo7wM .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-gIuHQQbHIRuzo7wM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gIuHQQbHIRuzo7wM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-gIuHQQbHIRuzo7wM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gIuHQQbHIRuzo7wM .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-gIuHQQbHIRuzo7wM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-gIuHQQbHIRuzo7wM .cluster text{fill:#333;}#mermaid-svg-gIuHQQbHIRuzo7wM .cluster span{color:#333;}#mermaid-svg-gIuHQQbHIRuzo7wM div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-gIuHQQbHIRuzo7wM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-gIuHQQbHIRuzo7wM rect.text{fill:none;stroke-width:0;}#mermaid-svg-gIuHQQbHIRuzo7wM .icon-shape,#mermaid-svg-gIuHQQbHIRuzo7wM .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gIuHQQbHIRuzo7wM .icon-shape p,#mermaid-svg-gIuHQQbHIRuzo7wM .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-gIuHQQbHIRuzo7wM .icon-shape .label rect,#mermaid-svg-gIuHQQbHIRuzo7wM .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gIuHQQbHIRuzo7wM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-gIuHQQbHIRuzo7wM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-gIuHQQbHIRuzo7wM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 目标: 6 元

面额 1, 3, 4
第一步选什么?
贪心: 选最大面额 4
剩余 2 元
只能选 1+1
结果: 4+1+1 = 3 枚 ❌
最优: 选 3
剩余 3 元
再选 3
结果: 3+3 = 2 枚 ✅
失败根因

选了 4 消耗 4 单位容量

未来无法再选 3
成功关键

选了 3 只消耗 3 单位

未来恰好还能选 3

1.4 一句话判断法

面试中的实用启发式:

如果你能对所有候选定义一个全序关系("谁比谁好"),并且"选了最好的不会影响后续继续选最好的"------大概率可以贪心。

如果你发现"选了 A 之后,原本第二好的 B 变得不可选了"------大概率不能贪心,退回 DP。

二、交换论证:面试中唯一需要掌握的证明方法

2.1 为什么面试需要"半证明"

"你怎么知道贪心是对的?"------这是 80% 面试官在你写完贪心代码后的第一个追问。

好消息:面试不需要你写黑板上的严格数学证明。坏消息:你也不能只说"感觉是对的"。

你需要的是一种介于直觉和严格证明之间的"半证明"------用 30 秒给出一个令人信服的论证,让面试官点头。

这种"半证明"有一个正式名字叫交换论证(Exchange Argument)。它是面试场景下唯一需要掌握的贪心证明方法。

2.2 交换论证的核心逻辑

思路极其简洁:

假设存在一个最优解 OPT,它没有采用我的贪心选择。我把 OPT 中的某个选择"换成"我的贪心选择------如果换完后解不会变差,那就说明"包含我的贪心选择的解也是最优的"→ 我的贪心选择是安全的。
#mermaid-svg-ybt1mA95fRsi9xYz{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ybt1mA95fRsi9xYz .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ybt1mA95fRsi9xYz .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ybt1mA95fRsi9xYz .error-icon{fill:#552222;}#mermaid-svg-ybt1mA95fRsi9xYz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ybt1mA95fRsi9xYz .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ybt1mA95fRsi9xYz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ybt1mA95fRsi9xYz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ybt1mA95fRsi9xYz .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ybt1mA95fRsi9xYz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ybt1mA95fRsi9xYz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ybt1mA95fRsi9xYz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ybt1mA95fRsi9xYz .marker.cross{stroke:#333333;}#mermaid-svg-ybt1mA95fRsi9xYz svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ybt1mA95fRsi9xYz p{margin:0;}#mermaid-svg-ybt1mA95fRsi9xYz .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ybt1mA95fRsi9xYz .cluster-label text{fill:#333;}#mermaid-svg-ybt1mA95fRsi9xYz .cluster-label span{color:#333;}#mermaid-svg-ybt1mA95fRsi9xYz .cluster-label span p{background-color:transparent;}#mermaid-svg-ybt1mA95fRsi9xYz .label text,#mermaid-svg-ybt1mA95fRsi9xYz span{fill:#333;color:#333;}#mermaid-svg-ybt1mA95fRsi9xYz .node rect,#mermaid-svg-ybt1mA95fRsi9xYz .node circle,#mermaid-svg-ybt1mA95fRsi9xYz .node ellipse,#mermaid-svg-ybt1mA95fRsi9xYz .node polygon,#mermaid-svg-ybt1mA95fRsi9xYz .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ybt1mA95fRsi9xYz .rough-node .label text,#mermaid-svg-ybt1mA95fRsi9xYz .node .label text,#mermaid-svg-ybt1mA95fRsi9xYz .image-shape .label,#mermaid-svg-ybt1mA95fRsi9xYz .icon-shape .label{text-anchor:middle;}#mermaid-svg-ybt1mA95fRsi9xYz .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ybt1mA95fRsi9xYz .rough-node .label,#mermaid-svg-ybt1mA95fRsi9xYz .node .label,#mermaid-svg-ybt1mA95fRsi9xYz .image-shape .label,#mermaid-svg-ybt1mA95fRsi9xYz .icon-shape .label{text-align:center;}#mermaid-svg-ybt1mA95fRsi9xYz .node.clickable{cursor:pointer;}#mermaid-svg-ybt1mA95fRsi9xYz .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ybt1mA95fRsi9xYz .arrowheadPath{fill:#333333;}#mermaid-svg-ybt1mA95fRsi9xYz .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ybt1mA95fRsi9xYz .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ybt1mA95fRsi9xYz .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ybt1mA95fRsi9xYz .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ybt1mA95fRsi9xYz .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ybt1mA95fRsi9xYz .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ybt1mA95fRsi9xYz .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ybt1mA95fRsi9xYz .cluster text{fill:#333;}#mermaid-svg-ybt1mA95fRsi9xYz .cluster span{color:#333;}#mermaid-svg-ybt1mA95fRsi9xYz div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ybt1mA95fRsi9xYz .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ybt1mA95fRsi9xYz rect.text{fill:none;stroke-width:0;}#mermaid-svg-ybt1mA95fRsi9xYz .icon-shape,#mermaid-svg-ybt1mA95fRsi9xYz .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ybt1mA95fRsi9xYz .icon-shape p,#mermaid-svg-ybt1mA95fRsi9xYz .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ybt1mA95fRsi9xYz .icon-shape .label rect,#mermaid-svg-ybt1mA95fRsi9xYz .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ybt1mA95fRsi9xYz .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ybt1mA95fRsi9xYz .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ybt1mA95fRsi9xYz :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是

设 OPT 是任意全局最优解
OPT 包含贪心选择 G?
直接成立 ✓
找到 OPT 中对应位置的选择 X
把 X 换成 G
证明换完后仍然合法

(不违反约束)
证明换完后目标值不変差

(≥ 原来)
结论: 存在包含 G 的最优解

贪心选择安全 ✓

2.3 完整演示:用排队打水走一遍

用一个人人都能秒懂的例子来展示交换论证的全过程。

问题:N 个人排队接水,每人接水时间分别是 t1, t2, ..., tn。只有一个水龙头。你可以安排任意顺序,求所有人等待时间之和的最小值。

(注意:第 k 个人的等待时间 = 前 k-1 个人的接水时间之和。总等待时间 = 所有人等待时间的累加。)

贪心策略:按接水时间从小到大排序------让接水快的人先来。

直觉:接水慢的人排前面,后面所有人都要多等很久。让"快的先上"可以减少后面每个人的等待。

但直觉不是证明。面试官想听的是:为什么这个策略一定是最优的?有没有可能某种"不完全从小到大"的排列恰好更好?

交换论证(四步完整版):

第 1 步:设 OPT 是任意最优解。即某个使总等待时间最小的排队顺序。

第 2 步:如果 OPT 恰好就是从小到大排 → 贪心 = 最优,直接成立。

第 3 步:如果 OPT 不是从小到大排 → 必然存在相邻逆序对。既然 OPT 不是完全升序,里面一定存在两个相邻的人 A 和 B,使得 A 排在 B 前面,但 tA > tB(A 比 B 慢,却排在前面)。

第 4 步:把 A 和 B 交换位置,看总等待时间的变化。

交换前后,A 和 B 之外的所有人的等待时间完全不变(他们前面排的人的总接水时间没变------只是 A 和 B 互换了,总量不变)。

变化只发生在 A 和 B 身上:

  • 交换前:B 要等 A 接完 → B 的等待包含 tA
  • 交换后:A 要等 B 接完 → A 的等待包含 tB
  • 净变化 = tB - tA < 0(因为 tB < tA)

总等待时间减少了!这和"OPT 是最优解"矛盾。

结论:最优解中不可能存在"慢的排在快的前面"的逆序对 → 最优解一定是从小到大排列 → 贪心策略正确。
#mermaid-svg-fdjH6xajV71fhiR7{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-fdjH6xajV71fhiR7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fdjH6xajV71fhiR7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fdjH6xajV71fhiR7 .error-icon{fill:#552222;}#mermaid-svg-fdjH6xajV71fhiR7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fdjH6xajV71fhiR7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fdjH6xajV71fhiR7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fdjH6xajV71fhiR7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fdjH6xajV71fhiR7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fdjH6xajV71fhiR7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fdjH6xajV71fhiR7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fdjH6xajV71fhiR7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fdjH6xajV71fhiR7 .marker.cross{stroke:#333333;}#mermaid-svg-fdjH6xajV71fhiR7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fdjH6xajV71fhiR7 p{margin:0;}#mermaid-svg-fdjH6xajV71fhiR7 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-fdjH6xajV71fhiR7 .cluster-label text{fill:#333;}#mermaid-svg-fdjH6xajV71fhiR7 .cluster-label span{color:#333;}#mermaid-svg-fdjH6xajV71fhiR7 .cluster-label span p{background-color:transparent;}#mermaid-svg-fdjH6xajV71fhiR7 .label text,#mermaid-svg-fdjH6xajV71fhiR7 span{fill:#333;color:#333;}#mermaid-svg-fdjH6xajV71fhiR7 .node rect,#mermaid-svg-fdjH6xajV71fhiR7 .node circle,#mermaid-svg-fdjH6xajV71fhiR7 .node ellipse,#mermaid-svg-fdjH6xajV71fhiR7 .node polygon,#mermaid-svg-fdjH6xajV71fhiR7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-fdjH6xajV71fhiR7 .rough-node .label text,#mermaid-svg-fdjH6xajV71fhiR7 .node .label text,#mermaid-svg-fdjH6xajV71fhiR7 .image-shape .label,#mermaid-svg-fdjH6xajV71fhiR7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-fdjH6xajV71fhiR7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-fdjH6xajV71fhiR7 .rough-node .label,#mermaid-svg-fdjH6xajV71fhiR7 .node .label,#mermaid-svg-fdjH6xajV71fhiR7 .image-shape .label,#mermaid-svg-fdjH6xajV71fhiR7 .icon-shape .label{text-align:center;}#mermaid-svg-fdjH6xajV71fhiR7 .node.clickable{cursor:pointer;}#mermaid-svg-fdjH6xajV71fhiR7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-fdjH6xajV71fhiR7 .arrowheadPath{fill:#333333;}#mermaid-svg-fdjH6xajV71fhiR7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-fdjH6xajV71fhiR7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-fdjH6xajV71fhiR7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fdjH6xajV71fhiR7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-fdjH6xajV71fhiR7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fdjH6xajV71fhiR7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-fdjH6xajV71fhiR7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-fdjH6xajV71fhiR7 .cluster text{fill:#333;}#mermaid-svg-fdjH6xajV71fhiR7 .cluster span{color:#333;}#mermaid-svg-fdjH6xajV71fhiR7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-fdjH6xajV71fhiR7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-fdjH6xajV71fhiR7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-fdjH6xajV71fhiR7 .icon-shape,#mermaid-svg-fdjH6xajV71fhiR7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fdjH6xajV71fhiR7 .icon-shape p,#mermaid-svg-fdjH6xajV71fhiR7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-fdjH6xajV71fhiR7 .icon-shape .label rect,#mermaid-svg-fdjH6xajV71fhiR7 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fdjH6xajV71fhiR7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-fdjH6xajV71fhiR7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-fdjH6xajV71fhiR7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 交换后
交换前
B 少等 tA - tB
... A(tA) → B(tB) ...

tA > tB
... B(tB) → A(tA) ...

tB < tA

为什么用这个例子?因为"交换"是物理动作------你真的能想象两个人换位置。换完后谁多等了、谁少等了、总账变好还是变差,一目了然。不需要理解"右端点""左端点"这些几何概念。

交换论证的本质永远是同一件事:假设最优解和我的贪心不同 → 找到不同的地方 → 交换 → 证明不会变差(甚至更好)→ 矛盾或结论成立。后面第三章区间题的证明也是这个框架,只是"交换"的对象从"两个相邻的人"变成了"两个区间"。

2.4 面试中的 30 秒表达

交换论证落到面试口头表达,不需要术语,只需要三句话的固定句式:

"假设最优解没有用我的贪心选择,而是用了一个更XX的选择 X。我把 X 换成我的------因为YYY,换完后不会违规;因为ZZZ,换完后总代价不会更差。所以我的选择至少和最优解一样好。"

两个例子的 30 秒版本:

  • 排队打水:"假设最优解里有个慢的 A 排在快的 B 前面。我把他俩换一下------换完后别人的等待不变,而 B 少等了(tA - tB)的时间,总等待减少了。所以最优解里不可能有逆序。"

  • 区间调度:"假设最优解第一个选了一个结束更晚的区间 X。我把 X 换成结束最早的 G------G 结束更早,不会和后面冲突;选的数量没变。所以换完不会更差。"

两段话结构完全一致:假设不同 → 交换 → 不违规 + 不变差 → 贪心安全。 记住这个句式,任何贪心题都能现场用。

2.5 交换论证的通用性

几乎所有贪心题的正确性都可以用交换论证来表述。模式是统一的:

  1. 假设最优解做了和我不同的选择
  2. 把那个选择"换成"我的贪心选择
  3. 论证:换完后不违规、不变差
  4. 结论:我的选择是安全的

不同题目的区别仅在于第 3 步的具体论证细节------但框架永远是这四步。

三、区间四件套:同一副牌的四种打法

区间类问题是贪心面试题中出镜率最高的题型。但很多人学完之后,面对一道新的区间题仍然犹豫------"这道题到底该按左端点排还是右端点排?"

原因在于:四道题的表面共性太强了。都是一堆区间,都需要排序,都是贪心,代码骨架都是"排序 + 一次扫描 + 一个 if 判断"。如果你记的是"第一道按右端点、第二道按左端点",那你记的是结论而不是逻辑------一换问法就混。

这一章的目标:建立一个统一的辨别框架,让你从题目的"问法"一步定位到策略,而不是靠记忆。

3.1 四道题分别在问什么

同样是一堆区间,同样是贪心,但四道题的"问法"完全不同------这决定了排序方式和决策逻辑的差异:

算法题 问的是什么
最大不相交区间数 从 N 个区间中选出最多的、互不重叠的子集
最少穿刺点数 选最少的点,使每个区间至少包含一个点
最小区间分组数 把所有区间分成最少的组,使组内无重叠
最少区间覆盖数 选最少的区间,完整覆盖一段给定的目标线段

四道题的表面共性太强------都是区间、都排序、都贪心、代码骨架都是"排序 + 扫描 + 一个 if"。区分它们的关键不是记排序方式,而是先认清"我在回答哪个问题"。

3.2 核心辨别法:一个问题分两族

拿到区间贪心题时,问自己一个问题:

"我是在从区间中'选'一些出来?还是在为所有区间做'安排'?"

这个问题把四道题一刀切成两族:

  • 第一族:"选"区间(从中挑出子集)

    • 最大不相交:从 N 个区间中选出最多的互不重叠子集
    • 最少穿刺:选最少的点,使每个区间被穿到
  • 第二族:"安排"区间(为所有区间做分配)

    • 最小分组:为所有区间安排房间(分组),使组内不冲突
    • 最少覆盖:安排最少的区间来覆盖目标线段

两族的排序方式恰好不同:

思维模式 排序方式 直觉
"选"区间 我挑走一些,留下剩余空间 按右端点升序 谁先走(结束早),谁先被考虑
"安排"区间 按到来顺序逐个安排 按左端点升序 谁先来,谁先被处理

记忆口诀:

选 → 右端点 (选结束早的,给后面留空间)

安排 → 左端点(来了就处理,当下做最优分配)

只记这一条规则,四道题的排序方式不再混淆。

3.3 第一族详解:"选"区间------按右端点排序

最大不相交区间数

问题:N 个区间,选最多的互不重叠区间。

贪心策略 :按右端点升序排序。维护一个变量 last_end(上一个选中区间的右端点)。遍历每个区间------如果它的左端点 > last_end,就选它(更新 last_end = 它的右端点)。

为什么按右端点?你在"选"区间,每选一个就"占用"一段时间轴。占用越少(右端点越小 = 结束越早),后面的空间越多,能选的后续区间就越多。选结束早的 = 给未来留余地 = 局部最优推动全局最优。
#mermaid-svg-EodEMpVKl1SPb1pj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-EodEMpVKl1SPb1pj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-EodEMpVKl1SPb1pj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-EodEMpVKl1SPb1pj .error-icon{fill:#552222;}#mermaid-svg-EodEMpVKl1SPb1pj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-EodEMpVKl1SPb1pj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-EodEMpVKl1SPb1pj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-EodEMpVKl1SPb1pj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-EodEMpVKl1SPb1pj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-EodEMpVKl1SPb1pj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-EodEMpVKl1SPb1pj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-EodEMpVKl1SPb1pj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-EodEMpVKl1SPb1pj .marker.cross{stroke:#333333;}#mermaid-svg-EodEMpVKl1SPb1pj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-EodEMpVKl1SPb1pj p{margin:0;}#mermaid-svg-EodEMpVKl1SPb1pj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-EodEMpVKl1SPb1pj .cluster-label text{fill:#333;}#mermaid-svg-EodEMpVKl1SPb1pj .cluster-label span{color:#333;}#mermaid-svg-EodEMpVKl1SPb1pj .cluster-label span p{background-color:transparent;}#mermaid-svg-EodEMpVKl1SPb1pj .label text,#mermaid-svg-EodEMpVKl1SPb1pj span{fill:#333;color:#333;}#mermaid-svg-EodEMpVKl1SPb1pj .node rect,#mermaid-svg-EodEMpVKl1SPb1pj .node circle,#mermaid-svg-EodEMpVKl1SPb1pj .node ellipse,#mermaid-svg-EodEMpVKl1SPb1pj .node polygon,#mermaid-svg-EodEMpVKl1SPb1pj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-EodEMpVKl1SPb1pj .rough-node .label text,#mermaid-svg-EodEMpVKl1SPb1pj .node .label text,#mermaid-svg-EodEMpVKl1SPb1pj .image-shape .label,#mermaid-svg-EodEMpVKl1SPb1pj .icon-shape .label{text-anchor:middle;}#mermaid-svg-EodEMpVKl1SPb1pj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-EodEMpVKl1SPb1pj .rough-node .label,#mermaid-svg-EodEMpVKl1SPb1pj .node .label,#mermaid-svg-EodEMpVKl1SPb1pj .image-shape .label,#mermaid-svg-EodEMpVKl1SPb1pj .icon-shape .label{text-align:center;}#mermaid-svg-EodEMpVKl1SPb1pj .node.clickable{cursor:pointer;}#mermaid-svg-EodEMpVKl1SPb1pj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-EodEMpVKl1SPb1pj .arrowheadPath{fill:#333333;}#mermaid-svg-EodEMpVKl1SPb1pj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-EodEMpVKl1SPb1pj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-EodEMpVKl1SPb1pj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EodEMpVKl1SPb1pj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-EodEMpVKl1SPb1pj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EodEMpVKl1SPb1pj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-EodEMpVKl1SPb1pj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-EodEMpVKl1SPb1pj .cluster text{fill:#333;}#mermaid-svg-EodEMpVKl1SPb1pj .cluster span{color:#333;}#mermaid-svg-EodEMpVKl1SPb1pj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-EodEMpVKl1SPb1pj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-EodEMpVKl1SPb1pj rect.text{fill:none;stroke-width:0;}#mermaid-svg-EodEMpVKl1SPb1pj .icon-shape,#mermaid-svg-EodEMpVKl1SPb1pj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EodEMpVKl1SPb1pj .icon-shape p,#mermaid-svg-EodEMpVKl1SPb1pj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-EodEMpVKl1SPb1pj .icon-shape .label rect,#mermaid-svg-EodEMpVKl1SPb1pj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EodEMpVKl1SPb1pj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-EodEMpVKl1SPb1pj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-EodEMpVKl1SPb1pj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 贪心选择过程
区间按右端点排序
A 1,3
B 2,4
C 3,6
D 5,7
选 A (1,3)

last_end=3
看 B (2,4)

左 2 < 3 → 冲突跳过
看 C (3,6)

左 3 = last_end → 不冲突

选 C, last_end=6
看 D (5,7)

左 5 < 6 → 冲突跳过
选中 A, C = 2 个 ✅

代码骨架:

cpp 复制代码
sort(intervals.begin(), intervals.end(), 
     [](auto& a, auto& b) { return a.right < b.right; });

int count = 0, last_end = -INF;
for (auto& [l, r] : intervals) {
    if (l > last_end) {
        count++;
        last_end = r;
    }
}
最少穿刺点数

问题:选最少的点,使每个区间至少包含一个点。

看起来和上一题完全不同------一个是"选区间",一个是"选点"。但答案恒等:

最大不相交区间数 = 最少穿刺点数

为什么?两个方向的论证:

  • ≤ 方向:如果你选出了 k 个互不重叠的区间,那至少需要 k 个点分别穿刺它们(因为不重叠 → 一个点最多穿一个)。所以穿刺点数 ≥ 最大不相交数。
  • ≥ 方向:用同样的贪心算法------按右端点排序,每当遇到一个"还没被穿刺"的区间时,选它的右端点作为穿刺点。每选一个点 = 找到了一个和之前不重叠的区间。所以穿刺点数 = 最大不相交数。

代码完全一样------上面那段代码中的 count 既是"选出的区间数"也是"需要的穿刺点数"。

面试加分表达:"这两个问题互为对偶。最大不相交区间数 = 最少穿刺点数,解法完全相同。"

3.4 第二族详解:"安排"区间------按左端点排序

最小区间分组数

问题:将所有区间分成若干组,组内无重叠,求最少组数。

现实比喻:所有客人都必须入住。你不能"不选"任何客人------只能给每位客人分配一个房间。问最少需要几个房间。

贪心策略:按左端点排序(按到达时间排队)。维护一个小根堆,堆中每个元素 = 某个组当前的最大右端点(该房间的最晚退房时间)。

遍历每个区间 [l, r]

  • 如果堆顶(最早空出的房间)< l → 该房间空了,复用它(pop 堆顶,push r)
  • 否则 → 所有房间都被占着,必须新开房间(直接 push r)

最终堆的大小 = 最少房间数。

为什么按左端点?你是"安排者"------客人按到达时间排队,来一个安排一个。不能让后到的客人插队(否则可能错过更优的分配)。按到来顺序处理是"安排"类问题的通用范式。

为什么对?每次新开房间,说明当前时刻有"堆大小"个区间在同时重叠。堆大小 = 当前时刻的最大重叠深度 = 最少需要的房间数(这是下界,而贪心恰好达到了下界)。

代码骨架:

cpp 复制代码
sort(intervals.begin(), intervals.end(),
     [](auto& a, auto& b) { return a.left < b.left; });

priority_queue<int, vector<int>, greater<int>> pq;
for (auto& [l, r] : intervals) {
    if (!pq.empty() && pq.top() < l) {
        pq.pop();
    }
    pq.push(r);
}
return pq.size();

#mermaid-svg-N4UeC8vdJ6q17U7b{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-N4UeC8vdJ6q17U7b .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-N4UeC8vdJ6q17U7b .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-N4UeC8vdJ6q17U7b .error-icon{fill:#552222;}#mermaid-svg-N4UeC8vdJ6q17U7b .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-N4UeC8vdJ6q17U7b .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-N4UeC8vdJ6q17U7b .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-N4UeC8vdJ6q17U7b .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-N4UeC8vdJ6q17U7b .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-N4UeC8vdJ6q17U7b .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-N4UeC8vdJ6q17U7b .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-N4UeC8vdJ6q17U7b .marker{fill:#333333;stroke:#333333;}#mermaid-svg-N4UeC8vdJ6q17U7b .marker.cross{stroke:#333333;}#mermaid-svg-N4UeC8vdJ6q17U7b svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-N4UeC8vdJ6q17U7b p{margin:0;}#mermaid-svg-N4UeC8vdJ6q17U7b .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-N4UeC8vdJ6q17U7b .cluster-label text{fill:#333;}#mermaid-svg-N4UeC8vdJ6q17U7b .cluster-label span{color:#333;}#mermaid-svg-N4UeC8vdJ6q17U7b .cluster-label span p{background-color:transparent;}#mermaid-svg-N4UeC8vdJ6q17U7b .label text,#mermaid-svg-N4UeC8vdJ6q17U7b span{fill:#333;color:#333;}#mermaid-svg-N4UeC8vdJ6q17U7b .node rect,#mermaid-svg-N4UeC8vdJ6q17U7b .node circle,#mermaid-svg-N4UeC8vdJ6q17U7b .node ellipse,#mermaid-svg-N4UeC8vdJ6q17U7b .node polygon,#mermaid-svg-N4UeC8vdJ6q17U7b .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-N4UeC8vdJ6q17U7b .rough-node .label text,#mermaid-svg-N4UeC8vdJ6q17U7b .node .label text,#mermaid-svg-N4UeC8vdJ6q17U7b .image-shape .label,#mermaid-svg-N4UeC8vdJ6q17U7b .icon-shape .label{text-anchor:middle;}#mermaid-svg-N4UeC8vdJ6q17U7b .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-N4UeC8vdJ6q17U7b .rough-node .label,#mermaid-svg-N4UeC8vdJ6q17U7b .node .label,#mermaid-svg-N4UeC8vdJ6q17U7b .image-shape .label,#mermaid-svg-N4UeC8vdJ6q17U7b .icon-shape .label{text-align:center;}#mermaid-svg-N4UeC8vdJ6q17U7b .node.clickable{cursor:pointer;}#mermaid-svg-N4UeC8vdJ6q17U7b .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-N4UeC8vdJ6q17U7b .arrowheadPath{fill:#333333;}#mermaid-svg-N4UeC8vdJ6q17U7b .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-N4UeC8vdJ6q17U7b .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-N4UeC8vdJ6q17U7b .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-N4UeC8vdJ6q17U7b .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-N4UeC8vdJ6q17U7b .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-N4UeC8vdJ6q17U7b .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-N4UeC8vdJ6q17U7b .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-N4UeC8vdJ6q17U7b .cluster text{fill:#333;}#mermaid-svg-N4UeC8vdJ6q17U7b .cluster span{color:#333;}#mermaid-svg-N4UeC8vdJ6q17U7b div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-N4UeC8vdJ6q17U7b .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-N4UeC8vdJ6q17U7b rect.text{fill:none;stroke-width:0;}#mermaid-svg-N4UeC8vdJ6q17U7b .icon-shape,#mermaid-svg-N4UeC8vdJ6q17U7b .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-N4UeC8vdJ6q17U7b .icon-shape p,#mermaid-svg-N4UeC8vdJ6q17U7b .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-N4UeC8vdJ6q17U7b .icon-shape .label rect,#mermaid-svg-N4UeC8vdJ6q17U7b .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-N4UeC8vdJ6q17U7b .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-N4UeC8vdJ6q17U7b .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-N4UeC8vdJ6q17U7b :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是, 房间已空闲
否, 所有房间被占
区间按左端点排序
遍历区间 l, r
堆顶最小右端点 < l?
pop 堆顶

复用该组
新开一组
push r 入堆
下一个区间
返回堆大小

= 最少组数

最少区间覆盖数

问题:用最少的区间覆盖目标线段 S, T

现实比喻:营业时段是 S, T,每个员工有一个可工作的时间段。你需要用最少的员工覆盖整个营业时段。

贪心策略 :按左端点排序。维护一个变量 start(当前需要被覆盖的起点,初始 = S)。每一轮:在所有左端点 ≤ start 的区间中,选右端点最大的那个 → 更新 start = 那个右端点。重复直到 start ≥ T

为什么按左端点?同样是"安排"类------你必须覆盖 start 这个位置,这是当前必须处理的任务。在所有能覆盖 start 的候选中,选最远的(一步走最远 → 步数最少)。

为什么对(交换论证):假设最优解在这一步选了一个不是"最远"的区间 X(右端点 < 贪心选的 G 的右端点)。把 X 换成 G------覆盖范围只会更大不会更小,后续需要覆盖的距离更短 → 后续步数 ≤ 原来。所以换完不会更差。贪心选择安全。

代码骨架:

cpp 复制代码
sort(intervals.begin(), intervals.end(),
     [](auto& a, auto& b) { return a.left < b.left; });

int start = S, count = 0, i = 0;
while (start < T) {
    int max_reach = -INF;
    while (i < n && intervals[i].left <= start) {
        max_reach = max(max_reach, intervals[i].right);
        i++;
    }
    if (max_reach == -INF) return -1;
    count++;
    start = max_reach;
}
return count;

#mermaid-svg-HbbJ0sGx7kOuB56V{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HbbJ0sGx7kOuB56V .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HbbJ0sGx7kOuB56V .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HbbJ0sGx7kOuB56V .error-icon{fill:#552222;}#mermaid-svg-HbbJ0sGx7kOuB56V .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HbbJ0sGx7kOuB56V .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HbbJ0sGx7kOuB56V .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HbbJ0sGx7kOuB56V .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HbbJ0sGx7kOuB56V .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HbbJ0sGx7kOuB56V .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HbbJ0sGx7kOuB56V .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HbbJ0sGx7kOuB56V .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HbbJ0sGx7kOuB56V .marker.cross{stroke:#333333;}#mermaid-svg-HbbJ0sGx7kOuB56V svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HbbJ0sGx7kOuB56V p{margin:0;}#mermaid-svg-HbbJ0sGx7kOuB56V .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HbbJ0sGx7kOuB56V .cluster-label text{fill:#333;}#mermaid-svg-HbbJ0sGx7kOuB56V .cluster-label span{color:#333;}#mermaid-svg-HbbJ0sGx7kOuB56V .cluster-label span p{background-color:transparent;}#mermaid-svg-HbbJ0sGx7kOuB56V .label text,#mermaid-svg-HbbJ0sGx7kOuB56V span{fill:#333;color:#333;}#mermaid-svg-HbbJ0sGx7kOuB56V .node rect,#mermaid-svg-HbbJ0sGx7kOuB56V .node circle,#mermaid-svg-HbbJ0sGx7kOuB56V .node ellipse,#mermaid-svg-HbbJ0sGx7kOuB56V .node polygon,#mermaid-svg-HbbJ0sGx7kOuB56V .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HbbJ0sGx7kOuB56V .rough-node .label text,#mermaid-svg-HbbJ0sGx7kOuB56V .node .label text,#mermaid-svg-HbbJ0sGx7kOuB56V .image-shape .label,#mermaid-svg-HbbJ0sGx7kOuB56V .icon-shape .label{text-anchor:middle;}#mermaid-svg-HbbJ0sGx7kOuB56V .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HbbJ0sGx7kOuB56V .rough-node .label,#mermaid-svg-HbbJ0sGx7kOuB56V .node .label,#mermaid-svg-HbbJ0sGx7kOuB56V .image-shape .label,#mermaid-svg-HbbJ0sGx7kOuB56V .icon-shape .label{text-align:center;}#mermaid-svg-HbbJ0sGx7kOuB56V .node.clickable{cursor:pointer;}#mermaid-svg-HbbJ0sGx7kOuB56V .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HbbJ0sGx7kOuB56V .arrowheadPath{fill:#333333;}#mermaid-svg-HbbJ0sGx7kOuB56V .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HbbJ0sGx7kOuB56V .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HbbJ0sGx7kOuB56V .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HbbJ0sGx7kOuB56V .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HbbJ0sGx7kOuB56V .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HbbJ0sGx7kOuB56V .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HbbJ0sGx7kOuB56V .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HbbJ0sGx7kOuB56V .cluster text{fill:#333;}#mermaid-svg-HbbJ0sGx7kOuB56V .cluster span{color:#333;}#mermaid-svg-HbbJ0sGx7kOuB56V div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-HbbJ0sGx7kOuB56V .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HbbJ0sGx7kOuB56V rect.text{fill:none;stroke-width:0;}#mermaid-svg-HbbJ0sGx7kOuB56V .icon-shape,#mermaid-svg-HbbJ0sGx7kOuB56V .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HbbJ0sGx7kOuB56V .icon-shape p,#mermaid-svg-HbbJ0sGx7kOuB56V .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HbbJ0sGx7kOuB56V .icon-shape .label rect,#mermaid-svg-HbbJ0sGx7kOuB56V .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HbbJ0sGx7kOuB56V .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HbbJ0sGx7kOuB56V .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HbbJ0sGx7kOuB56V :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否, 无区间覆盖 start

否, 继续
是, 覆盖完成
start = S

目标: 覆盖到 T
在左端点 ≤ start

的区间中
找右端点最大的区间
找到了吗?
返回 -1 (无解)
start = 该右端点

count++
start ≥ T?
返回 count

3.5 合并区间:不是贪心选择,是预处理

"合并区间"经常被放到区间题里一起讲,但它的性质不同------它不是在做选择或安排,而是在做预处理(把重叠的区间合并成一个大区间)。

策略:按左端点排序,遍历时维护当前区间的右端点。如果下一个区间的左端点 ≤ 当前右端点 → 合并(更新右端点 = max);否则 → 当前区间结束,开始新的一段。

它属于"扫描线"思想,不需要贪心证明------因为没有"选择",只有"合并"。

3.6 终极速查表

问题 排序 贪心决策 关键直觉
选最多不重叠 右端点↑ 选不冲突且结束最早的 结束早 → 留给后面的空间多
穿最少点 右端点↑ 未覆盖时选右端点 与上题对偶,解法相同
分最少组 左端点↑ 能复用已有组就复用 按到来顺序处理,不浪费资源
最少段覆盖 左端点↑ 选能盖 start 且右端点最远的 一步走最远 → 总步数最少
合并重叠 左端点↑ 重叠就合并 预处理,非贪心选择

3.7 面试三秒定位法

#mermaid-svg-lmpMTqjJc8Tp6oRS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-lmpMTqjJc8Tp6oRS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-lmpMTqjJc8Tp6oRS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-lmpMTqjJc8Tp6oRS .error-icon{fill:#552222;}#mermaid-svg-lmpMTqjJc8Tp6oRS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lmpMTqjJc8Tp6oRS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-lmpMTqjJc8Tp6oRS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lmpMTqjJc8Tp6oRS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lmpMTqjJc8Tp6oRS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-lmpMTqjJc8Tp6oRS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lmpMTqjJc8Tp6oRS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lmpMTqjJc8Tp6oRS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lmpMTqjJc8Tp6oRS .marker.cross{stroke:#333333;}#mermaid-svg-lmpMTqjJc8Tp6oRS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lmpMTqjJc8Tp6oRS p{margin:0;}#mermaid-svg-lmpMTqjJc8Tp6oRS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-lmpMTqjJc8Tp6oRS .cluster-label text{fill:#333;}#mermaid-svg-lmpMTqjJc8Tp6oRS .cluster-label span{color:#333;}#mermaid-svg-lmpMTqjJc8Tp6oRS .cluster-label span p{background-color:transparent;}#mermaid-svg-lmpMTqjJc8Tp6oRS .label text,#mermaid-svg-lmpMTqjJc8Tp6oRS span{fill:#333;color:#333;}#mermaid-svg-lmpMTqjJc8Tp6oRS .node rect,#mermaid-svg-lmpMTqjJc8Tp6oRS .node circle,#mermaid-svg-lmpMTqjJc8Tp6oRS .node ellipse,#mermaid-svg-lmpMTqjJc8Tp6oRS .node polygon,#mermaid-svg-lmpMTqjJc8Tp6oRS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-lmpMTqjJc8Tp6oRS .rough-node .label text,#mermaid-svg-lmpMTqjJc8Tp6oRS .node .label text,#mermaid-svg-lmpMTqjJc8Tp6oRS .image-shape .label,#mermaid-svg-lmpMTqjJc8Tp6oRS .icon-shape .label{text-anchor:middle;}#mermaid-svg-lmpMTqjJc8Tp6oRS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-lmpMTqjJc8Tp6oRS .rough-node .label,#mermaid-svg-lmpMTqjJc8Tp6oRS .node .label,#mermaid-svg-lmpMTqjJc8Tp6oRS .image-shape .label,#mermaid-svg-lmpMTqjJc8Tp6oRS .icon-shape .label{text-align:center;}#mermaid-svg-lmpMTqjJc8Tp6oRS .node.clickable{cursor:pointer;}#mermaid-svg-lmpMTqjJc8Tp6oRS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-lmpMTqjJc8Tp6oRS .arrowheadPath{fill:#333333;}#mermaid-svg-lmpMTqjJc8Tp6oRS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-lmpMTqjJc8Tp6oRS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-lmpMTqjJc8Tp6oRS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lmpMTqjJc8Tp6oRS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-lmpMTqjJc8Tp6oRS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lmpMTqjJc8Tp6oRS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-lmpMTqjJc8Tp6oRS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-lmpMTqjJc8Tp6oRS .cluster text{fill:#333;}#mermaid-svg-lmpMTqjJc8Tp6oRS .cluster span{color:#333;}#mermaid-svg-lmpMTqjJc8Tp6oRS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-lmpMTqjJc8Tp6oRS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-lmpMTqjJc8Tp6oRS rect.text{fill:none;stroke-width:0;}#mermaid-svg-lmpMTqjJc8Tp6oRS .icon-shape,#mermaid-svg-lmpMTqjJc8Tp6oRS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lmpMTqjJc8Tp6oRS .icon-shape p,#mermaid-svg-lmpMTqjJc8Tp6oRS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-lmpMTqjJc8Tp6oRS .icon-shape .label rect,#mermaid-svg-lmpMTqjJc8Tp6oRS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lmpMTqjJc8Tp6oRS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-lmpMTqjJc8Tp6oRS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-lmpMTqjJc8Tp6oRS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是


拿到区间贪心题
我在'选'区间/选点?

(从中挑子集)
按右端点排序

结束早的优先
我在'安排'所有区间?

(分组/覆盖/调度)
按左端点排序

来了就处理

四、深度案例:股票买卖------"贪心"二字从不出现,但它就是贪心

这道题在面试中几乎不会被归类为"贪心题"。它的标签通常是"数组""一次遍历""维护最小值"。但如果你用本文建立的框架来审视它,你会发现:它的内核就是贪心------DP 中的枚举被局部最优选择替代了。

这一节的目的不是教你写代码(代码只有 5 行),而是展示一个高阶思维路径:如何从暴力出发,通过分析"DP 的哪一层枚举是多余的"来发现贪心结构。

4.1 题目与暴力

给定股票价格数组 prices[],只允许买卖一次(先买后卖),求最大利润。

暴力解------枚举所有 (买入日 j, 卖出日 i) 对:

cpp 复制代码
int max_profit = 0;
for (int i = 0; i < n; i++) {
    for (int j = 0; j < i; j++) {
        max_profit = max(max_profit, prices[i] - prices[j]);
    }
}

O(n²)。正确但慢。

4.2 发现贪心结构:固定外层,审视内层

这里用到一个通用的优化技巧------"固定外层变量为常量,看内层循环在做什么"。

把 i(卖出日)当作常量,审视内层循环:

内层在 [0, i-1] 中找 prices[j] 的最小值。

好。现在 i 从 0 递增到 n-1。每次 i 增加 1,内层的搜索范围从 [0, i-2] 变成 [0, i-1]------范围增加了一个元素 prices[i-1]

关键问题:范围增加一个元素后,最小值怎么更新?

答案极其简单:拿新元素和旧的最小值比一下就行了。 如果新元素更小,更新最小值;否则最小值不变。
#mermaid-svg-CiUKfOvq9zELzqgk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CiUKfOvq9zELzqgk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CiUKfOvq9zELzqgk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CiUKfOvq9zELzqgk .error-icon{fill:#552222;}#mermaid-svg-CiUKfOvq9zELzqgk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CiUKfOvq9zELzqgk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CiUKfOvq9zELzqgk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CiUKfOvq9zELzqgk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CiUKfOvq9zELzqgk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CiUKfOvq9zELzqgk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CiUKfOvq9zELzqgk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CiUKfOvq9zELzqgk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CiUKfOvq9zELzqgk .marker.cross{stroke:#333333;}#mermaid-svg-CiUKfOvq9zELzqgk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CiUKfOvq9zELzqgk p{margin:0;}#mermaid-svg-CiUKfOvq9zELzqgk .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-CiUKfOvq9zELzqgk .cluster-label text{fill:#333;}#mermaid-svg-CiUKfOvq9zELzqgk .cluster-label span{color:#333;}#mermaid-svg-CiUKfOvq9zELzqgk .cluster-label span p{background-color:transparent;}#mermaid-svg-CiUKfOvq9zELzqgk .label text,#mermaid-svg-CiUKfOvq9zELzqgk span{fill:#333;color:#333;}#mermaid-svg-CiUKfOvq9zELzqgk .node rect,#mermaid-svg-CiUKfOvq9zELzqgk .node circle,#mermaid-svg-CiUKfOvq9zELzqgk .node ellipse,#mermaid-svg-CiUKfOvq9zELzqgk .node polygon,#mermaid-svg-CiUKfOvq9zELzqgk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CiUKfOvq9zELzqgk .rough-node .label text,#mermaid-svg-CiUKfOvq9zELzqgk .node .label text,#mermaid-svg-CiUKfOvq9zELzqgk .image-shape .label,#mermaid-svg-CiUKfOvq9zELzqgk .icon-shape .label{text-anchor:middle;}#mermaid-svg-CiUKfOvq9zELzqgk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CiUKfOvq9zELzqgk .rough-node .label,#mermaid-svg-CiUKfOvq9zELzqgk .node .label,#mermaid-svg-CiUKfOvq9zELzqgk .image-shape .label,#mermaid-svg-CiUKfOvq9zELzqgk .icon-shape .label{text-align:center;}#mermaid-svg-CiUKfOvq9zELzqgk .node.clickable{cursor:pointer;}#mermaid-svg-CiUKfOvq9zELzqgk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CiUKfOvq9zELzqgk .arrowheadPath{fill:#333333;}#mermaid-svg-CiUKfOvq9zELzqgk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CiUKfOvq9zELzqgk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CiUKfOvq9zELzqgk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CiUKfOvq9zELzqgk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CiUKfOvq9zELzqgk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CiUKfOvq9zELzqgk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CiUKfOvq9zELzqgk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CiUKfOvq9zELzqgk .cluster text{fill:#333;}#mermaid-svg-CiUKfOvq9zELzqgk .cluster span{color:#333;}#mermaid-svg-CiUKfOvq9zELzqgk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-CiUKfOvq9zELzqgk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CiUKfOvq9zELzqgk rect.text{fill:none;stroke-width:0;}#mermaid-svg-CiUKfOvq9zELzqgk .icon-shape,#mermaid-svg-CiUKfOvq9zELzqgk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CiUKfOvq9zELzqgk .icon-shape p,#mermaid-svg-CiUKfOvq9zELzqgk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CiUKfOvq9zELzqgk .icon-shape .label rect,#mermaid-svg-CiUKfOvq9zELzqgk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CiUKfOvq9zELzqgk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CiUKfOvq9zELzqgk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CiUKfOvq9zELzqgk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} i=0: 搜索 0, -1
i=1: 搜索 0,0

+1 个元素
i=2: 搜索 0,1

+1 个元素
i=3: 搜索 0,2

+1 个元素
... 范围只增不减
极值可增量维护

O(1) 一次比较

一次比较 = O(1)。内层循环消失了。

cpp 复制代码
int min_price = INT_MAX;
int max_profit = 0;
for (int i = 0; i < n; i++) {
    min_price = min(min_price, prices[i]);
    max_profit = max(max_profit, prices[i] - min_price);
}

O(n)。这就是最终答案。

4.3 为什么这是贪心

让我们用第一章的框架来审视这个优化过程:

  • DP 视角:dp[i] = 在第 i 天卖出时能获得的最大利润 = prices[i] - min(prices[0..i-1])
  • 枚举:min(prices[0..i-1]) 原本需要遍历所有 j < i
  • 贪心选择:min_price 是一个"贪心维护的局部最优"------它始终锁定当前为止的最低买入价
  • 枚举被消灭:因为"搜索范围只增不减,极值可增量维护"

DP 转移中的枚举退化为 O(1) 的贪心选择。这正是第一章说的"贪心 = DP 的降维"。

4.4 反直觉之处:为什么另一种迭代顺序不行

有人会想:我能不能固定买入日 j,内层找 [j+1, n-1] 中的最高卖出价?

问题在于:j 每增加 1,内层搜索范围 [j+1, n-1] 减少 了一个元素(元素 prices[j] 被移除了)。

减少元素时:如果被移除的恰好是当前最大值------最大值失效了,你不得不重新遍历整个范围。无法用 O(1) 增量维护。
#mermaid-svg-fpE6m2vpAWv8zao3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-fpE6m2vpAWv8zao3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fpE6m2vpAWv8zao3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fpE6m2vpAWv8zao3 .error-icon{fill:#552222;}#mermaid-svg-fpE6m2vpAWv8zao3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fpE6m2vpAWv8zao3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fpE6m2vpAWv8zao3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fpE6m2vpAWv8zao3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fpE6m2vpAWv8zao3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fpE6m2vpAWv8zao3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fpE6m2vpAWv8zao3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fpE6m2vpAWv8zao3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fpE6m2vpAWv8zao3 .marker.cross{stroke:#333333;}#mermaid-svg-fpE6m2vpAWv8zao3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fpE6m2vpAWv8zao3 p{margin:0;}#mermaid-svg-fpE6m2vpAWv8zao3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-fpE6m2vpAWv8zao3 .cluster-label text{fill:#333;}#mermaid-svg-fpE6m2vpAWv8zao3 .cluster-label span{color:#333;}#mermaid-svg-fpE6m2vpAWv8zao3 .cluster-label span p{background-color:transparent;}#mermaid-svg-fpE6m2vpAWv8zao3 .label text,#mermaid-svg-fpE6m2vpAWv8zao3 span{fill:#333;color:#333;}#mermaid-svg-fpE6m2vpAWv8zao3 .node rect,#mermaid-svg-fpE6m2vpAWv8zao3 .node circle,#mermaid-svg-fpE6m2vpAWv8zao3 .node ellipse,#mermaid-svg-fpE6m2vpAWv8zao3 .node polygon,#mermaid-svg-fpE6m2vpAWv8zao3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-fpE6m2vpAWv8zao3 .rough-node .label text,#mermaid-svg-fpE6m2vpAWv8zao3 .node .label text,#mermaid-svg-fpE6m2vpAWv8zao3 .image-shape .label,#mermaid-svg-fpE6m2vpAWv8zao3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-fpE6m2vpAWv8zao3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-fpE6m2vpAWv8zao3 .rough-node .label,#mermaid-svg-fpE6m2vpAWv8zao3 .node .label,#mermaid-svg-fpE6m2vpAWv8zao3 .image-shape .label,#mermaid-svg-fpE6m2vpAWv8zao3 .icon-shape .label{text-align:center;}#mermaid-svg-fpE6m2vpAWv8zao3 .node.clickable{cursor:pointer;}#mermaid-svg-fpE6m2vpAWv8zao3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-fpE6m2vpAWv8zao3 .arrowheadPath{fill:#333333;}#mermaid-svg-fpE6m2vpAWv8zao3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-fpE6m2vpAWv8zao3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-fpE6m2vpAWv8zao3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fpE6m2vpAWv8zao3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-fpE6m2vpAWv8zao3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fpE6m2vpAWv8zao3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-fpE6m2vpAWv8zao3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-fpE6m2vpAWv8zao3 .cluster text{fill:#333;}#mermaid-svg-fpE6m2vpAWv8zao3 .cluster span{color:#333;}#mermaid-svg-fpE6m2vpAWv8zao3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-fpE6m2vpAWv8zao3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-fpE6m2vpAWv8zao3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-fpE6m2vpAWv8zao3 .icon-shape,#mermaid-svg-fpE6m2vpAWv8zao3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fpE6m2vpAWv8zao3 .icon-shape p,#mermaid-svg-fpE6m2vpAWv8zao3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-fpE6m2vpAWv8zao3 .icon-shape .label rect,#mermaid-svg-fpE6m2vpAWv8zao3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fpE6m2vpAWv8zao3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-fpE6m2vpAWv8zao3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-fpE6m2vpAWv8zao3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 减元素 ❌ 贪心不成立
范围不断缩小
被移出的可能是极值
极值失效 → 重新扫描
内层循环消不掉
加元素 ✅ 贪心成立
范围只增不减
新元素和旧极值比较
O(1) 增量维护
内层循环可消灭

核心洞察:

加元素 → 极值只需增量比较(O(1))→ 内层循环可消灭 → 贪心成立

减元素 → 极值可能失效 → 需要重新扫描 → 内层循环消不掉 → 贪心不成立

这是一个通用的贪心可行性信号。当你发现某个内层循环的搜索范围"只增不减"时,大概率可以用贪心(增量维护)消灭它。

4.5 推广:同一洞察的其他化身

这个"搜索范围只增 → 极值可增量维护"的模式,在很多表面不叫"贪心"的题目中出现:

  • Kadane 算法(最大子数组和)dp[i] = max(dp[i-1] + a[i], a[i])dp[i-1] 如果 < 0 就丢弃------等价于"重新开始"。这里的贪心选择是:如果前面的累积是负担,就不要它。这个判断只需要 O(1)。

  • 接雨水的左侧最高柱 :遍历每个位置时维护 left_max = max(left_max, height[i])。搜索范围 [0, i] 只增不减 → 最大值可增量维护。

  • 单调栈中的"左边第一个更大元素":元素入栈(只增),极值通过栈顶 O(1) 获取。

#mermaid-svg-9ry2wjpLjNSf5oTT{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9ry2wjpLjNSf5oTT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9ry2wjpLjNSf5oTT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9ry2wjpLjNSf5oTT .error-icon{fill:#552222;}#mermaid-svg-9ry2wjpLjNSf5oTT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9ry2wjpLjNSf5oTT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9ry2wjpLjNSf5oTT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9ry2wjpLjNSf5oTT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9ry2wjpLjNSf5oTT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9ry2wjpLjNSf5oTT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9ry2wjpLjNSf5oTT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9ry2wjpLjNSf5oTT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9ry2wjpLjNSf5oTT .marker.cross{stroke:#333333;}#mermaid-svg-9ry2wjpLjNSf5oTT svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9ry2wjpLjNSf5oTT p{margin:0;}#mermaid-svg-9ry2wjpLjNSf5oTT .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-9ry2wjpLjNSf5oTT .cluster-label text{fill:#333;}#mermaid-svg-9ry2wjpLjNSf5oTT .cluster-label span{color:#333;}#mermaid-svg-9ry2wjpLjNSf5oTT .cluster-label span p{background-color:transparent;}#mermaid-svg-9ry2wjpLjNSf5oTT .label text,#mermaid-svg-9ry2wjpLjNSf5oTT span{fill:#333;color:#333;}#mermaid-svg-9ry2wjpLjNSf5oTT .node rect,#mermaid-svg-9ry2wjpLjNSf5oTT .node circle,#mermaid-svg-9ry2wjpLjNSf5oTT .node ellipse,#mermaid-svg-9ry2wjpLjNSf5oTT .node polygon,#mermaid-svg-9ry2wjpLjNSf5oTT .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9ry2wjpLjNSf5oTT .rough-node .label text,#mermaid-svg-9ry2wjpLjNSf5oTT .node .label text,#mermaid-svg-9ry2wjpLjNSf5oTT .image-shape .label,#mermaid-svg-9ry2wjpLjNSf5oTT .icon-shape .label{text-anchor:middle;}#mermaid-svg-9ry2wjpLjNSf5oTT .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9ry2wjpLjNSf5oTT .rough-node .label,#mermaid-svg-9ry2wjpLjNSf5oTT .node .label,#mermaid-svg-9ry2wjpLjNSf5oTT .image-shape .label,#mermaid-svg-9ry2wjpLjNSf5oTT .icon-shape .label{text-align:center;}#mermaid-svg-9ry2wjpLjNSf5oTT .node.clickable{cursor:pointer;}#mermaid-svg-9ry2wjpLjNSf5oTT .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9ry2wjpLjNSf5oTT .arrowheadPath{fill:#333333;}#mermaid-svg-9ry2wjpLjNSf5oTT .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9ry2wjpLjNSf5oTT .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9ry2wjpLjNSf5oTT .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9ry2wjpLjNSf5oTT .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9ry2wjpLjNSf5oTT .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9ry2wjpLjNSf5oTT .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9ry2wjpLjNSf5oTT .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9ry2wjpLjNSf5oTT .cluster text{fill:#333;}#mermaid-svg-9ry2wjpLjNSf5oTT .cluster span{color:#333;}#mermaid-svg-9ry2wjpLjNSf5oTT div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-9ry2wjpLjNSf5oTT .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9ry2wjpLjNSf5oTT rect.text{fill:none;stroke-width:0;}#mermaid-svg-9ry2wjpLjNSf5oTT .icon-shape,#mermaid-svg-9ry2wjpLjNSf5oTT .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9ry2wjpLjNSf5oTT .icon-shape p,#mermaid-svg-9ry2wjpLjNSf5oTT .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9ry2wjpLjNSf5oTT .icon-shape .label rect,#mermaid-svg-9ry2wjpLjNSf5oTT .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9ry2wjpLjNSf5oTT .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9ry2wjpLjNSf5oTT .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9ry2wjpLjNSf5oTT :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 优化方向: 消灭枚举
暴力枚举 O(n²)

枚举所有买入/卖出对
DP 视角

dpi = pricesi - min(prices0..i-1)
固定外层 i

审视内层

发现搜索范围只增不减
贪心 O(n)

min_price 增量更新

内层循环消灭!

它们的共同本质:原本需要一层枚举来获取的信息,因为搜索范围的单调扩张,退化为 O(1) 的增量维护。

五、面试实战:贪心 vs DP 的决策路径

5.1 三秒判断法

面试中拿到优化题,用这棵决策树:
#mermaid-svg-q5WiwOvYemy99Gwb{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-q5WiwOvYemy99Gwb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-q5WiwOvYemy99Gwb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-q5WiwOvYemy99Gwb .error-icon{fill:#552222;}#mermaid-svg-q5WiwOvYemy99Gwb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-q5WiwOvYemy99Gwb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-q5WiwOvYemy99Gwb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-q5WiwOvYemy99Gwb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-q5WiwOvYemy99Gwb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-q5WiwOvYemy99Gwb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-q5WiwOvYemy99Gwb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-q5WiwOvYemy99Gwb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-q5WiwOvYemy99Gwb .marker.cross{stroke:#333333;}#mermaid-svg-q5WiwOvYemy99Gwb svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-q5WiwOvYemy99Gwb p{margin:0;}#mermaid-svg-q5WiwOvYemy99Gwb .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-q5WiwOvYemy99Gwb .cluster-label text{fill:#333;}#mermaid-svg-q5WiwOvYemy99Gwb .cluster-label span{color:#333;}#mermaid-svg-q5WiwOvYemy99Gwb .cluster-label span p{background-color:transparent;}#mermaid-svg-q5WiwOvYemy99Gwb .label text,#mermaid-svg-q5WiwOvYemy99Gwb span{fill:#333;color:#333;}#mermaid-svg-q5WiwOvYemy99Gwb .node rect,#mermaid-svg-q5WiwOvYemy99Gwb .node circle,#mermaid-svg-q5WiwOvYemy99Gwb .node ellipse,#mermaid-svg-q5WiwOvYemy99Gwb .node polygon,#mermaid-svg-q5WiwOvYemy99Gwb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-q5WiwOvYemy99Gwb .rough-node .label text,#mermaid-svg-q5WiwOvYemy99Gwb .node .label text,#mermaid-svg-q5WiwOvYemy99Gwb .image-shape .label,#mermaid-svg-q5WiwOvYemy99Gwb .icon-shape .label{text-anchor:middle;}#mermaid-svg-q5WiwOvYemy99Gwb .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-q5WiwOvYemy99Gwb .rough-node .label,#mermaid-svg-q5WiwOvYemy99Gwb .node .label,#mermaid-svg-q5WiwOvYemy99Gwb .image-shape .label,#mermaid-svg-q5WiwOvYemy99Gwb .icon-shape .label{text-align:center;}#mermaid-svg-q5WiwOvYemy99Gwb .node.clickable{cursor:pointer;}#mermaid-svg-q5WiwOvYemy99Gwb .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-q5WiwOvYemy99Gwb .arrowheadPath{fill:#333333;}#mermaid-svg-q5WiwOvYemy99Gwb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-q5WiwOvYemy99Gwb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-q5WiwOvYemy99Gwb .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-q5WiwOvYemy99Gwb .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-q5WiwOvYemy99Gwb .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-q5WiwOvYemy99Gwb .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-q5WiwOvYemy99Gwb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-q5WiwOvYemy99Gwb .cluster text{fill:#333;}#mermaid-svg-q5WiwOvYemy99Gwb .cluster span{color:#333;}#mermaid-svg-q5WiwOvYemy99Gwb div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-q5WiwOvYemy99Gwb .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-q5WiwOvYemy99Gwb rect.text{fill:none;stroke-width:0;}#mermaid-svg-q5WiwOvYemy99Gwb .icon-shape,#mermaid-svg-q5WiwOvYemy99Gwb .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-q5WiwOvYemy99Gwb .icon-shape p,#mermaid-svg-q5WiwOvYemy99Gwb .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-q5WiwOvYemy99Gwb .icon-shape .label rect,#mermaid-svg-q5WiwOvYemy99Gwb .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-q5WiwOvYemy99Gwb .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-q5WiwOvYemy99Gwb .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-q5WiwOvYemy99Gwb :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 能
不能
不确定
直觉上每步选最好的能行?
能快速构造反例?
不能贪心 → 用 DP
大概率能贪心

准备好交换论证 30 秒版
先用 DP 安全

写完再看能否简化

5.2 经典"差一点就能贪心"的反例

问题 贪心直觉 反例 失败原因 正解
零钱兑换 1,3,4 凑 6 选最大面额 4+1+1=3 > 3+3=2 当前选择消耗了未来容量 DP
0-1 背包 选性价比最高的 性价比高的占满空间 一件挤掉两件 DP
LIS 每步选最小递增 可能错过更长的未来序列 需要全局视野 DP+贪心二分

这些例子的共性:当前选择和未来选择之间存在资源耦合。 选了 A 会让 B 不可选------而 B 可能属于全局最优解。

5.3 表达四步法

面试中讲贪心解法,按这个顺序说:

  1. 策略(一句话):"每次选右端点最小的不冲突区间"
  2. 排序(一句话):"按右端点升序排序,因为结束早 = 给后面留空间多"
  3. 正确性(交换论证 30 秒版):"假设最优解选了一个结束更晚的,换成我的不会冲突而且不会更差"
  4. 复杂度:"排序 O(n log n),扫描 O(n),总计 O(n log n)"

#mermaid-svg-ORVHjcz1epVkA2Wt{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ORVHjcz1epVkA2Wt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ORVHjcz1epVkA2Wt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ORVHjcz1epVkA2Wt .error-icon{fill:#552222;}#mermaid-svg-ORVHjcz1epVkA2Wt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ORVHjcz1epVkA2Wt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ORVHjcz1epVkA2Wt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ORVHjcz1epVkA2Wt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ORVHjcz1epVkA2Wt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ORVHjcz1epVkA2Wt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ORVHjcz1epVkA2Wt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ORVHjcz1epVkA2Wt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ORVHjcz1epVkA2Wt .marker.cross{stroke:#333333;}#mermaid-svg-ORVHjcz1epVkA2Wt svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ORVHjcz1epVkA2Wt p{margin:0;}#mermaid-svg-ORVHjcz1epVkA2Wt .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ORVHjcz1epVkA2Wt .cluster-label text{fill:#333;}#mermaid-svg-ORVHjcz1epVkA2Wt .cluster-label span{color:#333;}#mermaid-svg-ORVHjcz1epVkA2Wt .cluster-label span p{background-color:transparent;}#mermaid-svg-ORVHjcz1epVkA2Wt .label text,#mermaid-svg-ORVHjcz1epVkA2Wt span{fill:#333;color:#333;}#mermaid-svg-ORVHjcz1epVkA2Wt .node rect,#mermaid-svg-ORVHjcz1epVkA2Wt .node circle,#mermaid-svg-ORVHjcz1epVkA2Wt .node ellipse,#mermaid-svg-ORVHjcz1epVkA2Wt .node polygon,#mermaid-svg-ORVHjcz1epVkA2Wt .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ORVHjcz1epVkA2Wt .rough-node .label text,#mermaid-svg-ORVHjcz1epVkA2Wt .node .label text,#mermaid-svg-ORVHjcz1epVkA2Wt .image-shape .label,#mermaid-svg-ORVHjcz1epVkA2Wt .icon-shape .label{text-anchor:middle;}#mermaid-svg-ORVHjcz1epVkA2Wt .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ORVHjcz1epVkA2Wt .rough-node .label,#mermaid-svg-ORVHjcz1epVkA2Wt .node .label,#mermaid-svg-ORVHjcz1epVkA2Wt .image-shape .label,#mermaid-svg-ORVHjcz1epVkA2Wt .icon-shape .label{text-align:center;}#mermaid-svg-ORVHjcz1epVkA2Wt .node.clickable{cursor:pointer;}#mermaid-svg-ORVHjcz1epVkA2Wt .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ORVHjcz1epVkA2Wt .arrowheadPath{fill:#333333;}#mermaid-svg-ORVHjcz1epVkA2Wt .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ORVHjcz1epVkA2Wt .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ORVHjcz1epVkA2Wt .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ORVHjcz1epVkA2Wt .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ORVHjcz1epVkA2Wt .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ORVHjcz1epVkA2Wt .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ORVHjcz1epVkA2Wt .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ORVHjcz1epVkA2Wt .cluster text{fill:#333;}#mermaid-svg-ORVHjcz1epVkA2Wt .cluster span{color:#333;}#mermaid-svg-ORVHjcz1epVkA2Wt div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ORVHjcz1epVkA2Wt .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ORVHjcz1epVkA2Wt rect.text{fill:none;stroke-width:0;}#mermaid-svg-ORVHjcz1epVkA2Wt .icon-shape,#mermaid-svg-ORVHjcz1epVkA2Wt .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ORVHjcz1epVkA2Wt .icon-shape p,#mermaid-svg-ORVHjcz1epVkA2Wt .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ORVHjcz1epVkA2Wt .icon-shape .label rect,#mermaid-svg-ORVHjcz1epVkA2Wt .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ORVHjcz1epVkA2Wt .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ORVHjcz1epVkA2Wt .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ORVHjcz1epVkA2Wt :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1. 策略

每次选结束最早的不冲突区间
2. 排序

按右端点升序

结束早 = 给后面留空间
3. 正确性

交换论证 30 秒版

换成我的不会更差
4. 复杂度

排序 O(n log n)

扫描 O(n)

5.4 面试官追问速答

追问 回答策略
"你怎么证明对?" 交换论证 30 秒版
"有反例吗?" "我构造了最小用例验证过,策略成立"
"能用 DP 做吗?" "能。贪心是 DP 在这道题上的退化------最优决策恰好就是局部最优"
"条件变了呢?" 分析"变化是否破坏贪心选择性质"------如果破坏,退回 DP

结尾

贪心是 DP 的精准降维。

DP 说"我枚举所有决策取最优"------贪心说"我能证明某个决策永远不比别的差,所以直接取它,省去枚举"。从 DP 到贪心,中间隔着一个数学判据(贪心选择性质)和一个证明方法(交换论证)。

全文三个核心认知:

  1. 贪心不是猜测,是证明。 它能用的前提是"贪心选择性质"------不是"看起来选最大的就行",而是"能证明选了这个不会错过全局最优"。面试中用交换论证的 30 秒版本:假设最优解没选我的,换进去不会更差。

  2. 区间四件套用一个问题区分。 "我在选区间,还是在安排区间?"选区间 → 按右端点(谁先走谁先选);安排区间 → 按左端点(谁先来先处理)。记住这一条,四道题不再混淆。

  3. 贪心的深层本质是"消灭 DP 中多余的枚举"。 股票买卖只有 5 行代码,但它背后的思维路径------"搜索范围只增不减 → 极值可增量维护 → 内层循环消失"------是一个通用的贪心发现方法。当你在一道题中发现"某层枚举的搜索范围只扩张不收缩"时,尝试用增量维护代替枚举------大概率就是贪心的入口。

不确定时用 DP------确定时用贪心。贪心是对 DP 的降维打击,前提是你有武器(证明)在手。

历史文章(算法解析专栏)

算法:双指针:从 O(n²) 到 O(n) 的核心武器

算法:链表题核心技巧与解题框架

算法:栈 - 不是容器,而是历史压缩工具

算法:动态规划 - 从暴力递归到高效填表