程序员面试宝典

一站式面试准备平台

返回分类
algorithms中级

回溯算法

深入理解回溯的本质:什么是"选择"与"撤销选择"、如何画出递归决策树

2026-04-15
阅读时间: 8分钟

回溯算法

回溯是面试中最考察"思维框架"的算法。关键不是背代码,而是理解递归决策树如何枚举所有可能

面试考察点

面试官通过这道题想考察:
1. 你是否能画出问题的递归决策树
2. 你是否能正确实现"选择"和"撤销选择"
3. 你是否能进行剪枝优化
4. 你是否能区分回溯和 DFS/BFS

一、回溯的本质

1.1 什么是回溯?(小白解释)

面试官追问:"什么是回溯?为什么叫回溯?"

回溯 = 枚举 + 撤销

想象走迷宫:
1. 选一条路走
2. 走到死路 → 回退到上一个路口
3. 选另一条路
4. 重复直到找到出口

这不就是"回退" + "继续探索" = 回溯!

回溯的关键词:
- 选择:做一件事
- 撤销:把这件事 undo
- 继续:尝试下一个选择

1.2 回溯的决策树(核心)

面试官追问:"怎么理解回溯的递归决策树?"

以"生成括号"为例:n=3

递归决策树:

""
├── "("
│   ├── "(( "
│   │   ├── "(((" → 添加 ")" → "((())"
│   │   └── ...
│   └── "(("
│       └── ...
└── ...

每一步都有选择:
- 可以加左括号(如果还有)
- 可以加右括号(如果左括号数 > 右括号数)

叶子节点 = 完整解

1.3 回溯的通用模板

def backtrack(路径, 选择列表):
    if 满足结束条件:
        结果.add(路径)
        return

    for 选择 in 选择列表:
        # 做选择
        路径.add(选择)
        做选择(选择列表)

        # 回溯(撤销选择)
        路径.remove(选择)
        撤销选择(选择列表)

二、子集问题

2.1 问题:求所有子集

考察点:最基本的回溯

问题:求数组 [1,2,3] 的所有子集

决策树:

        [] (空集)
       /  |  \
      1   2   3
     /|   |    |
   12 13  23   (只有叶子节点是解?错!)

正确决策树:

         []          根节点也是子集!
        / \
       1   (跳过1)
      / \
    12  (跳过2)
    /
  123

所有节点都是子集!

结果:[], [1], [2], [3], [1,2], [1,3], [2,3], [1,2,3]

2.2 去重技巧

问题:有重复元素时如何去重?

示例:[1,1,2]

关键:相同元素,前一个没用过就不能用后一个

排序后:
[1,1,2]

决策时跳过重复:
if i > start and nums[i] == nums[i-1]: continue

三、排列问题

3.1 全排列

考察点:回溯 + 标记

问题:求 [1,2,3] 的全排列

决策树:

         []
       /  |  \
      1   2   3
     /|  /|   /|
   12 21 13 31 23 32  ← 叶子节点

关键:需要一个 used 数组标记已使用的元素

3.2 全排列 II(去重)

问题:有重复元素的全排列

排序后,相同元素相邻
去重条件:
if used[i-1] == false: continue
// 解释:前一个相同元素没有被用过,所以跳过

四、面试真题讲解

4.1 括号生成(LeetCode 22)

考察点:回溯的应用

问题:生成 n 对括号的所有有效组合

思路:
- 左括号随时可以加(如果有剩余)
- 右括号只能在左括号比右括号多时加

递归决策:
def backtrack(path, left, right):
    if left == right == n:
        结果.add(path)
        return

    # 可以加左括号
    if left < n:
        backtrack(path + '(', left + 1, right)

    # 可以加右括号
    if left > right:
        backtrack(path + ')', left, right + 1)

4.2 N 皇后(LeetCode 51)

考察点:回溯 + 约束检测

问题:在 N×N 棋盘上放 N 个皇后,不能互相攻击

约束:
- 同一行只能有一个皇后
- 同一列只能有一个皇后
- 对角线只能有一个皇后

回溯思路:
逐行放置,检查当前位置是否安全
如果不安全就回退

五、面试总结

回溯的核心

┌─────────────────────────────────────────────────┐
│                    回溯算法                       │
├─────────────────────────────────────────────────┤
│                                                  │
│  本质:枚举 + 撤销                               │
│                                                  │
│  三要素:                                        │
│  1. 路径:已经做的选择                           │
│  2. 选择列表:当前可以做的选择                   │
│  3. 结束条件:到达叶子节点                       │
│                                                  │
│  优化:                                          │
│  - 剪枝:提前判断不合法的情况                    │
│  - 去重:排序后跳过重复                         │
│                                                  │
│  应用:                                          │
│  - 子集问题                                     │
│  - 排列问题                                     │
│  - 组合问题                                     │
│                                                  │
└─────────────────────────────────────────────────┘

面试能加分的回答

1. 能画出递归决策树
   "回溯的问题都可以用递归决策树表示"

2. 能正确实现撤销操作
   "撤销是回溯的灵魂,必须正确实现"

3. 能进行剪枝优化
   "提前判断不合法情况,减少搜索空间"

相关题目

相关标签