贪心算法:从直觉到证明
开篇
面试官给了一道"区间调度"------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 交换论证的通用性
几乎所有贪心题的正确性都可以用交换论证来表述。模式是统一的:
- 假设最优解做了和我不同的选择
- 把那个选择"换成"我的贪心选择
- 论证:换完后不违规、不变差
- 结论:我的选择是安全的
不同题目的区别仅在于第 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 表达四步法
面试中讲贪心解法,按这个顺序说:
- 策略(一句话):"每次选右端点最小的不冲突区间"
- 排序(一句话):"按右端点升序排序,因为结束早 = 给后面留空间多"
- 正确性(交换论证 30 秒版):"假设最优解选了一个结束更晚的,换成我的不会冲突而且不会更差"
- 复杂度:"排序 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 到贪心,中间隔着一个数学判据(贪心选择性质)和一个证明方法(交换论证)。
全文三个核心认知:
-
贪心不是猜测,是证明。 它能用的前提是"贪心选择性质"------不是"看起来选最大的就行",而是"能证明选了这个不会错过全局最优"。面试中用交换论证的 30 秒版本:假设最优解没选我的,换进去不会更差。
-
区间四件套用一个问题区分。 "我在选区间,还是在安排区间?"选区间 → 按右端点(谁先走谁先选);安排区间 → 按左端点(谁先来先处理)。记住这一条,四道题不再混淆。
-
贪心的深层本质是"消灭 DP 中多余的枚举"。 股票买卖只有 5 行代码,但它背后的思维路径------"搜索范围只增不减 → 极值可增量维护 → 内层循环消失"------是一个通用的贪心发现方法。当你在一道题中发现"某层枚举的搜索范围只扩张不收缩"时,尝试用增量维护代替枚举------大概率就是贪心的入口。
不确定时用 DP------确定时用贪心。贪心是对 DP 的降维打击,前提是你有武器(证明)在手。
历史文章(算法解析专栏)