深度优先搜索
DFS(Depth First Search)中文常叫“深搜”。它的特点是:先沿着一条路走到底,走不动就回退,再换一条路继续走。
DFS 特别适合解决:连通块、找路径、枚举方案、回溯、判环等问题。
------
1. DFS 的核心思想
- 把“当前状态”当作一个点
- 从当前点出发,尝试走到下一个点
- 走过的点要标记(避免无限循环)
- 走到不能走时回退(这就是回溯)
------
2. DFS 常用模板
2.1 图上的 DFS(邻接表)
void dfs(int u){
vis[u]=1; // 1) 标记 u 已访问
for(int v:adj[u]){ // 2) 枚举 u 的所有邻居 v
if(!vis[v]) dfs(v); // 3) 没访问过才递归
}
}2.2 网格迷宫 DFS(上下左右)
int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
void dfs(int x,int y){
vis[x][y]=1; // 标记访问
for(int k=0;k<4;k++){
int nx=x+dx[k], ny=y+dy[k];
if(nx<1||nx>n||ny<1||ny>m) continue; // 边界
if(vis[nx][ny]) continue; // 已访问
if(g[nx][ny]=='#') continue; // 墙不能走
dfs(nx,ny);
}
}------
3. 连通块
3.1 图上连通块
图上连通块:在图中用 DFS 给可达点标记,统计连通块数量、大小等。
做法:
1. 遍历所有点
2. 如果某点没访问过,说明发现一个新连通块
3.
cnt++,从它 DFS,把整块点都标记掉void dfs(int u){
vis[u]=1;
for(int v:adj[u]) if(!vis[v]) dfs(v);
}
int cnt=0;
for(int u=1;u<=n;u++){
if(!vis[u]){
cnt++;
dfs(u);
}
}如果要统计每块大小,在 dfs 里加
size++:void dfs(int u){
vis[u]=1;
size++;
for(int v:adj[u]) if(!vis[v]) dfs(v);
}------
3.2 迷宫连通块
迷宫连通块:把迷宫看成网格图,用 DFS 统计相连的空地区域数目。
int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
void dfs(int x,int y){
vis[x][y]=1;
for(int k=0;k<4;k++){
int nx=x+dx[k], ny=y+dy[k];
if(nx<1||nx>n||ny<1||ny>m) continue;
if(vis[nx][ny]) continue;
if(g[nx][ny]=='#') continue;
dfs(nx,ny);
}
}
int cnt=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(g[i][j]=='.' && !vis[i][j]){
cnt++;
dfs(i,j);
}
}
}------
4. 判环
4.1 无向图判环
结论: DFS 时遇到已访问但不是父亲的结点 $\Rightarrow$ 有环。
bool hasCycle=false;
void dfs(int u,int fa){
vis[u]=1;
for(int v:adj[u]){
if(!vis[v]){
dfs(v,u);
}else if(v!=fa){
hasCycle=true;
}
}
}------
4.2 有向图判环
三色标记:
0:未访问1:正在访问(在递归栈里)2:已访问完成DFS 中如果走到
state[v]==1,说明回到了递归栈中的点,有环。bool hasCycle=false;
void dfs(int u){
state[u]=1;
for(int v:adj[u]){
if(state[v]==0) dfs(v);
else if(state[v]==1) hasCycle=true;
}
state[u]=2;
}------
5. 搜索
5.1 图上搜索(是否存在路径)
从
s DFS,能访问到 t 说明存在路径。void dfs(int u){
vis[u]=1;
for(int v:adj[u]) if(!vis[v]) dfs(v);
}
// dfs(s) 后判断 vis[t]------
5.2 迷宫搜索(能否到达终点)
void dfs(int x,int y){
vis[x][y]=1;
for(int k=0;k<4;k++){
int nx=x+dx[k], ny=y+dy[k];
if(nx<1||nx>n||ny<1||ny>m) continue;
if(vis[nx][ny]) continue;
if(g[nx][ny]=='#') continue;
dfs(nx,ny);
}
}
// dfs(sx,sy) 后判断 vis[tx][ty]------
5.3 带回溯搜索(枚举方案)
特点: 每层做选择,做完选择后递归,回来要撤销选择。
void dfs(int step){
if(step==n){
check();
return;
}
for(each choice){
do_choice();
dfs(step+1);
undo_choice();
}
}------
6. 折半搜索(Meet in the middle)
把一次大搜索拆成“前一半 + 后一半”两部分:
dfs1(pos,end,sum){
if(pos==end){ store(sum); return; }
dfs1(pos+1,end,sum)
dfs1(pos+1,end,sum+a[pos]);
dfs2(pos,end,sum){
if(pos==end){
if(exist(target-sum)) ok=true;
return;
}
dfs2(pos+1,end,sum);
dfs2(pos+1,end,su
}------
7. 暴力枚举
把“枚举所有方案”的过程写成 DFS:
void dfs(int i){
if(i==n){
check();
return;
}
// 例:每个位置选 0 或 1
choose0();
dfs(i+1);
choose1();
dfs(i+1);
}【前置知识点】
1、基础数据结构
2、递归
【后置衔接知识点】
1、最近公共祖先
【思维导图】

【题目知识点分类】
01
Welcome24ever 和奶牛
普及-
--
练习
02
Welcome24ever 和牛奶
普及-
--
练习
03
新闻传播
普及-
--
练习
04
迷宫之判定
普及-
--
练习
05
选数
普及-
--
练习
06
迷宫之方案数
普及-
--
练习
07
入门
普及-
--
练习
08
亲戚
普及-
--
练习
09
全排列问题
普及-
--
练习
10
迷宫之最少花费时间
普及-
--
练习
11
迷宫
普及-
--
练习
12
Welcome24ever 和益智游戏
普及/提高-
--
练习
13
最少学习费用
普及/提高-
--
练习
14
朋友分组
普及/提高-
--
练习
15
五子棋对弈
普及/提高-
--
练习
16
Welcome24ever 和火车游戏
普及/提高-
--
练习
17
Welcome24ever 和网格迷宫
普及/提高-
--
练习
18
Welcome24ever 和代数
普及/提高-
--
练习
19
数的划分
普及/提高-
--
练习
20
组题目
普及/提高-
--
练习
21
[GESP202403 七级] 交流问题
普及/提高-
--
练习
22
取数游戏
普及/提高-
--
练习
24
单词接龙
普及/提高-
--
练习
25
最大连通块
普及+/提高
--
练习
26
最大模意义下子序列
普及+/提高
--
练习
27
Welcome24ever 和袜子
普及+/提高
--
练习
28
幻想迷宫
普及+/提高
--
练习
29
Welcome24ever 和山丘
普及+/提高
--
练习
30
Welcome24ever 和方块
提高+/省选-
--
练习