DFS 回溯

一条道走到黑,走不通就回头——深度优先搜索用递归 + 回溯扫遍所有可能。

CSP-J

◎学完你会

1动一动:DFS 走格子

5×5 网格,从 (0,0) 出发按“右下左上”优先递归走:能进就进,走到头退回再试别的方向。 点「走一步」看递归栈怎么进/退:

初始在 (0,0)。点「走一步」开始 DFS。
绿色=当前格,浅黄=已访问,深黄=正在回溯(从这条道退出来)。

2关键命令:DFS 回溯三步

void dfs(int x, int y){
  if(越界 || 已访问) return;
  vis[x][y] = 1; // ① 标记
  dfs(x, y+1); dfs(x+1, y); // ② 递归四个方向
  dfs(x, y-1); dfs(x-1, y);
  vis[x][y] = 0; // ③ 回溯:撤销标记
}
全排列本质一样:used[i]=1 → dfs(step+1) → used[i]=0。回溯就是把这次尝试的状态还原,好让兄弟分支重来。
访问过的点要记录:搜过的格子不进第二次,否则递归无限套娃。

⚠易错点

忘回溯(忘撤销标记):标记后不 vis[x][y]=0,兄弟分支会以为这格已被占,漏解。
漏写递归出口:没有“越界 / 已访问 / 到目标”任一出口,DFS 无限递归直到爆栈。
方向顺序想当然:DFS 的“走法顺序”决定路径形态,但结果覆盖所有可达格,别依赖某一种顺序。
回溯改了共享状态忘还原:数组、used 标记在递归前改、递归后必须还原,否则污染后续分支。

?跨学科:DFS 就是“钻到底再回头”

下迷宫/走山洞:先沿着一条岔路走到头,没路了就退回来试另一条——正是 DFS。 整理抽屉一层层翻到最底、再逐层还原,也是“递归 + 回溯”。

深度优先适合“要找遍所有可能 / 一路推进到深处”的探索;要找“最近/最短路”则看 BFS(下一节)。

✎练一练

DFS 里 vis[x][y]=0(撤销标记)的作用是?
撤销标记 = 回溯,把这次占用让出来,供另一条分支重试;否则该格被永久占住、漏解。
想找“从起点到终点的最少步数”,应该用 DFS 还是 BFS?
答案:用 BFS。DFS 一条道走到黑,先找到的路径不一定最短;BFS 按层扩散,第一次到达终点的那层就是最短路。DFS 更适合“找是否存在/枚举所有解”。