回溯算法三板斧---掌握「回溯三问」思考模板快速入门回溯

这是一个系列介绍了回溯的"回溯三问",还有三种对应的模板题型分别是子集型,组合型,排列型。感兴趣去我主页查看Leetcode专栏
#mermaid-svg-8h6xdCYVZkhsqCdG{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-8h6xdCYVZkhsqCdG .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8h6xdCYVZkhsqCdG .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8h6xdCYVZkhsqCdG .error-icon{fill:#552222;}#mermaid-svg-8h6xdCYVZkhsqCdG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8h6xdCYVZkhsqCdG .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8h6xdCYVZkhsqCdG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8h6xdCYVZkhsqCdG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8h6xdCYVZkhsqCdG .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8h6xdCYVZkhsqCdG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8h6xdCYVZkhsqCdG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8h6xdCYVZkhsqCdG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8h6xdCYVZkhsqCdG .marker.cross{stroke:#333333;}#mermaid-svg-8h6xdCYVZkhsqCdG svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8h6xdCYVZkhsqCdG p{margin:0;}#mermaid-svg-8h6xdCYVZkhsqCdG .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8h6xdCYVZkhsqCdG .cluster-label text{fill:#333;}#mermaid-svg-8h6xdCYVZkhsqCdG .cluster-label span{color:#333;}#mermaid-svg-8h6xdCYVZkhsqCdG .cluster-label span p{background-color:transparent;}#mermaid-svg-8h6xdCYVZkhsqCdG .label text,#mermaid-svg-8h6xdCYVZkhsqCdG span{fill:#333;color:#333;}#mermaid-svg-8h6xdCYVZkhsqCdG .node rect,#mermaid-svg-8h6xdCYVZkhsqCdG .node circle,#mermaid-svg-8h6xdCYVZkhsqCdG .node ellipse,#mermaid-svg-8h6xdCYVZkhsqCdG .node polygon,#mermaid-svg-8h6xdCYVZkhsqCdG .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8h6xdCYVZkhsqCdG .rough-node .label text,#mermaid-svg-8h6xdCYVZkhsqCdG .node .label text,#mermaid-svg-8h6xdCYVZkhsqCdG .image-shape .label,#mermaid-svg-8h6xdCYVZkhsqCdG .icon-shape .label{text-anchor:middle;}#mermaid-svg-8h6xdCYVZkhsqCdG .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8h6xdCYVZkhsqCdG .rough-node .label,#mermaid-svg-8h6xdCYVZkhsqCdG .node .label,#mermaid-svg-8h6xdCYVZkhsqCdG .image-shape .label,#mermaid-svg-8h6xdCYVZkhsqCdG .icon-shape .label{text-align:center;}#mermaid-svg-8h6xdCYVZkhsqCdG .node.clickable{cursor:pointer;}#mermaid-svg-8h6xdCYVZkhsqCdG .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8h6xdCYVZkhsqCdG .arrowheadPath{fill:#333333;}#mermaid-svg-8h6xdCYVZkhsqCdG .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8h6xdCYVZkhsqCdG .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8h6xdCYVZkhsqCdG .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8h6xdCYVZkhsqCdG .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8h6xdCYVZkhsqCdG .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8h6xdCYVZkhsqCdG .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8h6xdCYVZkhsqCdG .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8h6xdCYVZkhsqCdG .cluster text{fill:#333;}#mermaid-svg-8h6xdCYVZkhsqCdG .cluster span{color:#333;}#mermaid-svg-8h6xdCYVZkhsqCdG 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-8h6xdCYVZkhsqCdG .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8h6xdCYVZkhsqCdG rect.text{fill:none;stroke-width:0;}#mermaid-svg-8h6xdCYVZkhsqCdG .icon-shape,#mermaid-svg-8h6xdCYVZkhsqCdG .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8h6xdCYVZkhsqCdG .icon-shape p,#mermaid-svg-8h6xdCYVZkhsqCdG .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8h6xdCYVZkhsqCdG .icon-shape .label rect,#mermaid-svg-8h6xdCYVZkhsqCdG .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8h6xdCYVZkhsqCdG .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8h6xdCYVZkhsqCdG .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8h6xdCYVZkhsqCdG :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 🎯 回溯三问
🧩 子集型

选或不选
🔗 组合型

从 n 选 k
🔄 排列型

顺序有关

回溯算法入门:掌握「回溯三问」思考模板

为什么需要回溯?

先看一个问题:生成长为 n 的字符串,第一位从 {a, b, c} 中选,第二位从 {d, e, f} 中选,以此类推,输出所有组合。

当 n = 2 时,一个二重循环轻松搞定。但当 n 是一个未知参数时,你没法在代码里写 n 层 for 循环------循环嵌套的表达能力是有上限的,而回溯正是为了解决这个问题。

递归 vs 回溯:一句话说清关系

这可能是初学者最常问的问题。答案很简单:递归是编程技巧,回溯是算法思想,回溯通常用递归实现。

回溯的核心特征是「增量构造答案」------一步一步搭建结果,走到尽头或发现走不通时就回退,尝试另一条路。整个过程可以画成一棵搜索树:
#mermaid-svg-Uqy3ddJDDbrAVDtv{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-Uqy3ddJDDbrAVDtv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Uqy3ddJDDbrAVDtv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Uqy3ddJDDbrAVDtv .error-icon{fill:#552222;}#mermaid-svg-Uqy3ddJDDbrAVDtv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Uqy3ddJDDbrAVDtv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Uqy3ddJDDbrAVDtv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Uqy3ddJDDbrAVDtv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Uqy3ddJDDbrAVDtv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Uqy3ddJDDbrAVDtv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Uqy3ddJDDbrAVDtv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Uqy3ddJDDbrAVDtv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Uqy3ddJDDbrAVDtv .marker.cross{stroke:#333333;}#mermaid-svg-Uqy3ddJDDbrAVDtv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Uqy3ddJDDbrAVDtv p{margin:0;}#mermaid-svg-Uqy3ddJDDbrAVDtv .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Uqy3ddJDDbrAVDtv .cluster-label text{fill:#333;}#mermaid-svg-Uqy3ddJDDbrAVDtv .cluster-label span{color:#333;}#mermaid-svg-Uqy3ddJDDbrAVDtv .cluster-label span p{background-color:transparent;}#mermaid-svg-Uqy3ddJDDbrAVDtv .label text,#mermaid-svg-Uqy3ddJDDbrAVDtv span{fill:#333;color:#333;}#mermaid-svg-Uqy3ddJDDbrAVDtv .node rect,#mermaid-svg-Uqy3ddJDDbrAVDtv .node circle,#mermaid-svg-Uqy3ddJDDbrAVDtv .node ellipse,#mermaid-svg-Uqy3ddJDDbrAVDtv .node polygon,#mermaid-svg-Uqy3ddJDDbrAVDtv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Uqy3ddJDDbrAVDtv .rough-node .label text,#mermaid-svg-Uqy3ddJDDbrAVDtv .node .label text,#mermaid-svg-Uqy3ddJDDbrAVDtv .image-shape .label,#mermaid-svg-Uqy3ddJDDbrAVDtv .icon-shape .label{text-anchor:middle;}#mermaid-svg-Uqy3ddJDDbrAVDtv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Uqy3ddJDDbrAVDtv .rough-node .label,#mermaid-svg-Uqy3ddJDDbrAVDtv .node .label,#mermaid-svg-Uqy3ddJDDbrAVDtv .image-shape .label,#mermaid-svg-Uqy3ddJDDbrAVDtv .icon-shape .label{text-align:center;}#mermaid-svg-Uqy3ddJDDbrAVDtv .node.clickable{cursor:pointer;}#mermaid-svg-Uqy3ddJDDbrAVDtv .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Uqy3ddJDDbrAVDtv .arrowheadPath{fill:#333333;}#mermaid-svg-Uqy3ddJDDbrAVDtv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Uqy3ddJDDbrAVDtv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Uqy3ddJDDbrAVDtv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Uqy3ddJDDbrAVDtv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Uqy3ddJDDbrAVDtv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Uqy3ddJDDbrAVDtv .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Uqy3ddJDDbrAVDtv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Uqy3ddJDDbrAVDtv .cluster text{fill:#333;}#mermaid-svg-Uqy3ddJDDbrAVDtv .cluster span{color:#333;}#mermaid-svg-Uqy3ddJDDbrAVDtv 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-Uqy3ddJDDbrAVDtv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Uqy3ddJDDbrAVDtv rect.text{fill:none;stroke-width:0;}#mermaid-svg-Uqy3ddJDDbrAVDtv .icon-shape,#mermaid-svg-Uqy3ddJDDbrAVDtv .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Uqy3ddJDDbrAVDtv .icon-shape p,#mermaid-svg-Uqy3ddJDDbrAVDtv .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Uqy3ddJDDbrAVDtv .icon-shape .label rect,#mermaid-svg-Uqy3ddJDDbrAVDtv .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Uqy3ddJDDbrAVDtv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Uqy3ddJDDbrAVDtv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Uqy3ddJDDbrAVDtv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} path = \[\]

🔵 根:尚未选择
path0 = 'a'

🟡 第一步:从 {a,b,c} 选a
path0 = 'b'

🟡 第一步:从 {a,b,c} 选b
path0 = 'c'

🟡 第一步:从 {a,b,c} 选c
path = 'a','d'

🟢 完成答案:ad
path = 'a','e'

🟢 完成答案:ae
path = 'a','f'

🟢 完成答案:af
path = 'b','d'

🟢 完成答案:bd
path = 'b','e'

🟢 完成答案:be
path = 'b','f'

🟢 完成答案:bf
path = 'c','d'

🟢 完成答案:cd
path = 'c','e'

🟢 完成答案:ce
path = 'c','f'

🟢 完成答案:cf

从根到叶子,每条路径就是一个答案。回溯本质上是对这棵树做 DFS(深度优先搜索) ,这也是递归函数取名叫 dfs 的原因。

但新的问题来了:递归逻辑到底怎么写?很多人试图在脑子里跑完整棵树,想着每一层怎么"递"下去、怎么"归"上来,想着想着就乱了。

其实你根本不需要这样。只要边界条件和非边界条件的逻辑写对了,交给数学归纳法,递归代码就一定能得到正确结果。 关键在于------怎么把"这一块逻辑"写对。

回溯三问:一个通用思考框架

这是我思考回溯问题和动态规划问题的固定模板。三个问题依次回答,代码框架就自然出来了:
#mermaid-svg-oHtTNDgHIE2aJWad{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-oHtTNDgHIE2aJWad .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-oHtTNDgHIE2aJWad .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-oHtTNDgHIE2aJWad .error-icon{fill:#552222;}#mermaid-svg-oHtTNDgHIE2aJWad .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oHtTNDgHIE2aJWad .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-oHtTNDgHIE2aJWad .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oHtTNDgHIE2aJWad .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oHtTNDgHIE2aJWad .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-oHtTNDgHIE2aJWad .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oHtTNDgHIE2aJWad .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oHtTNDgHIE2aJWad .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oHtTNDgHIE2aJWad .marker.cross{stroke:#333333;}#mermaid-svg-oHtTNDgHIE2aJWad svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oHtTNDgHIE2aJWad p{margin:0;}#mermaid-svg-oHtTNDgHIE2aJWad .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-oHtTNDgHIE2aJWad .cluster-label text{fill:#333;}#mermaid-svg-oHtTNDgHIE2aJWad .cluster-label span{color:#333;}#mermaid-svg-oHtTNDgHIE2aJWad .cluster-label span p{background-color:transparent;}#mermaid-svg-oHtTNDgHIE2aJWad .label text,#mermaid-svg-oHtTNDgHIE2aJWad span{fill:#333;color:#333;}#mermaid-svg-oHtTNDgHIE2aJWad .node rect,#mermaid-svg-oHtTNDgHIE2aJWad .node circle,#mermaid-svg-oHtTNDgHIE2aJWad .node ellipse,#mermaid-svg-oHtTNDgHIE2aJWad .node polygon,#mermaid-svg-oHtTNDgHIE2aJWad .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oHtTNDgHIE2aJWad .rough-node .label text,#mermaid-svg-oHtTNDgHIE2aJWad .node .label text,#mermaid-svg-oHtTNDgHIE2aJWad .image-shape .label,#mermaid-svg-oHtTNDgHIE2aJWad .icon-shape .label{text-anchor:middle;}#mermaid-svg-oHtTNDgHIE2aJWad .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-oHtTNDgHIE2aJWad .rough-node .label,#mermaid-svg-oHtTNDgHIE2aJWad .node .label,#mermaid-svg-oHtTNDgHIE2aJWad .image-shape .label,#mermaid-svg-oHtTNDgHIE2aJWad .icon-shape .label{text-align:center;}#mermaid-svg-oHtTNDgHIE2aJWad .node.clickable{cursor:pointer;}#mermaid-svg-oHtTNDgHIE2aJWad .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-oHtTNDgHIE2aJWad .arrowheadPath{fill:#333333;}#mermaid-svg-oHtTNDgHIE2aJWad .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-oHtTNDgHIE2aJWad .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-oHtTNDgHIE2aJWad .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oHtTNDgHIE2aJWad .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-oHtTNDgHIE2aJWad .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oHtTNDgHIE2aJWad .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-oHtTNDgHIE2aJWad .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-oHtTNDgHIE2aJWad .cluster text{fill:#333;}#mermaid-svg-oHtTNDgHIE2aJWad .cluster span{color:#333;}#mermaid-svg-oHtTNDgHIE2aJWad 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-oHtTNDgHIE2aJWad .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-oHtTNDgHIE2aJWad rect.text{fill:none;stroke-width:0;}#mermaid-svg-oHtTNDgHIE2aJWad .icon-shape,#mermaid-svg-oHtTNDgHIE2aJWad .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oHtTNDgHIE2aJWad .icon-shape p,#mermaid-svg-oHtTNDgHIE2aJWad .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-oHtTNDgHIE2aJWad .icon-shape .label rect,#mermaid-svg-oHtTNDgHIE2aJWad .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oHtTNDgHIE2aJWad .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-oHtTNDgHIE2aJWad .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-oHtTNDgHIE2aJWad :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 转化为递归调用
🔴 第一问

当前操作是什么?

这一步递归

具体要做什么
🟡 第二问

子问题是什么?

定义 dfs(i)

要完成什么任务
🔵 第三问

下一个子问题是什么?

做完当前操作后

问题规模如何缩小
✅ 代码框架自然成形

下面逐问展开,结合例题来理解。

第一问:当前操作是什么?

明确这一步递归中,具体要做什么事情。

通常的回答是:「枚举当前位置可以填的所有选项」。比如构造字符串时,当前操作就是枚举第 i 个位置可以填哪个字母。用一个全局数组 path 来记录递归路径中已经枚举的内容,当前这一步就是往 path[i] 中填入一个选项。

第二问:子问题是什么?

定义 dfs(i) 这个函数要完成什么任务。

这里有一个非常关键的认知:参数 i 的含义不是「处理第 i 个」,而是「下标大于等于 i 的部分还需要处理」。

也就是说,dfs(i) 的职责是「把从 i 开始一直到结尾的这部分构造完成」。前面已经填好的部分不用管,只聚焦于剩余未处理的部分。

这一定义让子问题的语义非常自然------dfs(0) 就是从零开始构造整个答案,dfs(n) 就是什么都没剩、已经构造完成。

第三问:下一个子问题是什么?

做完当前操作后,问题规模如何缩小,递归调用应该怎么传参。

回答了第二问,这一问就水到渠成:枚举完第 i 个位置后,剩余的任务是处理从 i+1 开始的部分,所以递归调用 dfs(i+1)

这其实就是把原问题缩小为规模更小的同构子问题------回溯和递归的结构在这里完全统一。

三问的完整逻辑链

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

还有未尝试的选项
调用 dfs(0)

从零开始构造
进入 dfs(i)
i == n ?

(边界条件)
✅ 找到一个完整答案

加入结果集
🔴 当前操作

枚举 pathi 的所有可能
选择一个选项

填入 pathi
🔵 下一个子问题

递归调用 dfs(i+1)
递归返回,准备枚举下一个选项
返回上一层

实战:电话号码的字母组合

来看看回溯三问在实际题目中怎么用。

LeetCode 17 :给定仅包含数字 2-9 的字符串,返回所有字母组合。数字到字母的映射:2→abc,3→def 等。

输入 "23",输出 ["ad","ae","af","bd","be","bf","cd","ce","cf"]

三问拆解

第一问:当前操作 → 枚举 path[i] 要填入的字母。对于数字 2,枚举 a/b/c;对于数字 3,枚举 d/e/f。

第二问:子问题dfs(i) 负责构造下标 ≥ i 的部分。也就是说,参数 i 的含义是「还剩多少没处理完」。

第三问:下一个子问题 → 填入 path[i] 后,剩余任务从 i+1 开始 → 调用 dfs(i+1)

代码

java 复制代码
class Solution {
    private static final String[] MAPPING = new String[] { "", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv","wxyz" };

    public List<String> letterCombinations(String digits) {
        int n=digits.length();
        if(n==0){
            return List.of();
        }

        List<String> ans=new ArrayList<>();
        char[] path=new char[n];
        dfs(0,ans,path,digits.toCharArray());
        return ans;
    }
    private void dfs(int i,List<String> ans,char[] path,char[] digits){
        if(i==digits.length){
            ans.add(new String(path));
            return;
        }
        String letters=MAPPING[digits[i]-'0'];
        for(char c:letters.toCharArray()){
            path[i]=c;
            dfs(i+1,ans,path,digits);
        }
    }
}

注意这里没有显式的「恢复现场」操作,因为 path[i] 在下一轮循环中会被直接覆盖写入,不需要额外恢复。

递归执行流程

#mermaid-svg-z54XDirwcovLVFsA{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-z54XDirwcovLVFsA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-z54XDirwcovLVFsA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-z54XDirwcovLVFsA .error-icon{fill:#552222;}#mermaid-svg-z54XDirwcovLVFsA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-z54XDirwcovLVFsA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-z54XDirwcovLVFsA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-z54XDirwcovLVFsA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-z54XDirwcovLVFsA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-z54XDirwcovLVFsA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-z54XDirwcovLVFsA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-z54XDirwcovLVFsA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-z54XDirwcovLVFsA .marker.cross{stroke:#333333;}#mermaid-svg-z54XDirwcovLVFsA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-z54XDirwcovLVFsA p{margin:0;}#mermaid-svg-z54XDirwcovLVFsA .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-z54XDirwcovLVFsA .cluster-label text{fill:#333;}#mermaid-svg-z54XDirwcovLVFsA .cluster-label span{color:#333;}#mermaid-svg-z54XDirwcovLVFsA .cluster-label span p{background-color:transparent;}#mermaid-svg-z54XDirwcovLVFsA .label text,#mermaid-svg-z54XDirwcovLVFsA span{fill:#333;color:#333;}#mermaid-svg-z54XDirwcovLVFsA .node rect,#mermaid-svg-z54XDirwcovLVFsA .node circle,#mermaid-svg-z54XDirwcovLVFsA .node ellipse,#mermaid-svg-z54XDirwcovLVFsA .node polygon,#mermaid-svg-z54XDirwcovLVFsA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-z54XDirwcovLVFsA .rough-node .label text,#mermaid-svg-z54XDirwcovLVFsA .node .label text,#mermaid-svg-z54XDirwcovLVFsA .image-shape .label,#mermaid-svg-z54XDirwcovLVFsA .icon-shape .label{text-anchor:middle;}#mermaid-svg-z54XDirwcovLVFsA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-z54XDirwcovLVFsA .rough-node .label,#mermaid-svg-z54XDirwcovLVFsA .node .label,#mermaid-svg-z54XDirwcovLVFsA .image-shape .label,#mermaid-svg-z54XDirwcovLVFsA .icon-shape .label{text-align:center;}#mermaid-svg-z54XDirwcovLVFsA .node.clickable{cursor:pointer;}#mermaid-svg-z54XDirwcovLVFsA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-z54XDirwcovLVFsA .arrowheadPath{fill:#333333;}#mermaid-svg-z54XDirwcovLVFsA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-z54XDirwcovLVFsA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-z54XDirwcovLVFsA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-z54XDirwcovLVFsA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-z54XDirwcovLVFsA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-z54XDirwcovLVFsA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-z54XDirwcovLVFsA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-z54XDirwcovLVFsA .cluster text{fill:#333;}#mermaid-svg-z54XDirwcovLVFsA .cluster span{color:#333;}#mermaid-svg-z54XDirwcovLVFsA 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-z54XDirwcovLVFsA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-z54XDirwcovLVFsA rect.text{fill:none;stroke-width:0;}#mermaid-svg-z54XDirwcovLVFsA .icon-shape,#mermaid-svg-z54XDirwcovLVFsA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-z54XDirwcovLVFsA .icon-shape p,#mermaid-svg-z54XDirwcovLVFsA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-z54XDirwcovLVFsA .icon-shape .label rect,#mermaid-svg-z54XDirwcovLVFsA .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-z54XDirwcovLVFsA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-z54XDirwcovLVFsA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-z54XDirwcovLVFsA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} dfs(0)
i=0, digits0='2'

枚举 {a,b,c}
path0='a' → dfs(1)
path0='b' → dfs(1)
path0='c' → dfs(1)
i=1, digits1='3'

枚举 {d,e,f}
path1='d' → dfs(2) → ✓ 'ad'
path1='e' → dfs(2) → ✓ 'ae'
path1='f' → dfs(2) → ✓ 'af'
i=1, digits1='3'

枚举 {d,e,f}
path1='d' → dfs(2) → ✓ 'bd'
path1='e' → dfs(2) → ✓ 'be'
path1='f' → dfs(2) → ✓ 'bf'
i=1, digits1='3'

枚举 {d,e,f}
path1='d' → dfs(2) → ✓ 'cd'
path1='e' → dfs(2) → ✓ 'ce'
path1='f' → dfs(2) → ✓ 'cf'

总结

回溯三问的核心价值在于:帮你把注意力锁定在「当前这一步」,而不是在脑中跑完整棵递归树。
#mermaid-svg-TqhSwvFVANQ110sB{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-TqhSwvFVANQ110sB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TqhSwvFVANQ110sB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TqhSwvFVANQ110sB .error-icon{fill:#552222;}#mermaid-svg-TqhSwvFVANQ110sB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TqhSwvFVANQ110sB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TqhSwvFVANQ110sB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TqhSwvFVANQ110sB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TqhSwvFVANQ110sB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TqhSwvFVANQ110sB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TqhSwvFVANQ110sB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TqhSwvFVANQ110sB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TqhSwvFVANQ110sB .marker.cross{stroke:#333333;}#mermaid-svg-TqhSwvFVANQ110sB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TqhSwvFVANQ110sB p{margin:0;}#mermaid-svg-TqhSwvFVANQ110sB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TqhSwvFVANQ110sB .cluster-label text{fill:#333;}#mermaid-svg-TqhSwvFVANQ110sB .cluster-label span{color:#333;}#mermaid-svg-TqhSwvFVANQ110sB .cluster-label span p{background-color:transparent;}#mermaid-svg-TqhSwvFVANQ110sB .label text,#mermaid-svg-TqhSwvFVANQ110sB span{fill:#333;color:#333;}#mermaid-svg-TqhSwvFVANQ110sB .node rect,#mermaid-svg-TqhSwvFVANQ110sB .node circle,#mermaid-svg-TqhSwvFVANQ110sB .node ellipse,#mermaid-svg-TqhSwvFVANQ110sB .node polygon,#mermaid-svg-TqhSwvFVANQ110sB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TqhSwvFVANQ110sB .rough-node .label text,#mermaid-svg-TqhSwvFVANQ110sB .node .label text,#mermaid-svg-TqhSwvFVANQ110sB .image-shape .label,#mermaid-svg-TqhSwvFVANQ110sB .icon-shape .label{text-anchor:middle;}#mermaid-svg-TqhSwvFVANQ110sB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TqhSwvFVANQ110sB .rough-node .label,#mermaid-svg-TqhSwvFVANQ110sB .node .label,#mermaid-svg-TqhSwvFVANQ110sB .image-shape .label,#mermaid-svg-TqhSwvFVANQ110sB .icon-shape .label{text-align:center;}#mermaid-svg-TqhSwvFVANQ110sB .node.clickable{cursor:pointer;}#mermaid-svg-TqhSwvFVANQ110sB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TqhSwvFVANQ110sB .arrowheadPath{fill:#333333;}#mermaid-svg-TqhSwvFVANQ110sB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TqhSwvFVANQ110sB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TqhSwvFVANQ110sB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TqhSwvFVANQ110sB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TqhSwvFVANQ110sB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TqhSwvFVANQ110sB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TqhSwvFVANQ110sB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TqhSwvFVANQ110sB .cluster text{fill:#333;}#mermaid-svg-TqhSwvFVANQ110sB .cluster span{color:#333;}#mermaid-svg-TqhSwvFVANQ110sB 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-TqhSwvFVANQ110sB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TqhSwvFVANQ110sB rect.text{fill:none;stroke-width:0;}#mermaid-svg-TqhSwvFVANQ110sB .icon-shape,#mermaid-svg-TqhSwvFVANQ110sB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TqhSwvFVANQ110sB .icon-shape p,#mermaid-svg-TqhSwvFVANQ110sB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TqhSwvFVANQ110sB .icon-shape .label rect,#mermaid-svg-TqhSwvFVANQ110sB .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TqhSwvFVANQ110sB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TqhSwvFVANQ110sB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TqhSwvFVANQ110sB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 🧠 面对回溯问题时的思维过程
🔴 当前操作

枚举 pathi 的所有选项
🟡 子问题

dfs(i) 负责完成 ≥i 的部分
🔵 下一个子问题

dfs(i+1) 缩小问题规模
✅ 非边界逻辑:循环枚举 + 填充 + 递归
✅ 边界逻辑:i == n 时记录答案

几个关键认知:

  • i 的含义:不是「处理第 i 个」,而是「≥ i 的部分还需要枚举」------这一定义是理解整套模板的钥匙。
  • 边界条件 :当 i == n 时,意味着所有位置都已填完,当前 path 就是一个完整答案。
  • 恢复现场 :当 path 是全局可变共享结构时(如 List),递归返回后需要恢复到递归前的状态。但如果像上面那样用数组 + 下标覆盖写入,就不需要显式恢复。

掌握了回溯三问,你就拥有了一把拆解回溯问题的手术刀。下一篇将在这个框架上展开,讲解子集型回溯的两种经典思路------输入视角和答案视角,同样的三问,稍加变化就能解决完全不同的问题。


参考:灵茶山艾府《基础算法精讲 14 - 回溯算法套路①子集型回溯》


相关推荐
深圳市快瞳科技有限公司20 小时前
宠物生物特征识别:鼻纹、面部与多模态融合技术的突破
人工智能·算法·计算机视觉·宠物
格兰芬多呼神护卫20 小时前
# Agent 企业评估方案深度分析:从“看答案”到持续质量闭环_码士教育
java·开发语言·jvm
豆角焖肉20 小时前
冒泡、快排、堆排的实现逻辑与选择思路
java·算法·排序算法·快排·冒泡·堆排
番茄炒鸡蛋加糖21 小时前
Spring 事务传播机制 & 事务失效场景
java·后端·spring
青皮桔21 小时前
Redis AOF 文件损坏修复记录
运维·数据库·redis
qq_1715388521 小时前
深入浅出 MyBatis XML:从入门到精通
xml·java·mybatis
happyh h h h p p p p21 小时前
LVS 项目完整知识点总结
linux·服务器·数据库
冻柠檬飞冰走茶21 小时前
PTA基础编程题目集 7-24 约分最简分式(C语言实现)
c语言·开发语言·数据结构·算法
zander25821 小时前
LeetCode 78. 子集
算法·leetcode·深度优先