

一、核心思路
在二维字符网格中寻找一条路径,可以按顺序拼出目标单词。行走规则:仅能上下左右相邻移动,同一个单元格不能在同一条路径重复使用。
-
遍历网格每一个格子,如果当前格子字符等于单词首字符,以此位置作为起点启动 DFS;
-
DFS 参数:当前坐标
r行、c列,idx表示当前需要匹配单词的第idx位字符; -
终止条件:
idx == word.length():单词全部匹配完成,返回true;- 坐标越界,或者当前格子字符和
word[idx]不相等:此路径不通,返回false;
-
回溯标记:原地将当前格子修改为
#,标记为本轮路径已访问;递归返回后恢复原始字符,撤销标记,不影响其他分支搜索; -
四个方向搜索:上、下、左、右,使用
||短路或,只要任意一个方向返回 true,整体结果为 true。
二、完整 Java 代码
arduino
class Solution {
public boolean exist(char[][] board, String word) {
int m = board.length;
int n = board[0].length;
for(int i = 0; i < m; i++) {
for(int j = 0; j < n; j++) {
if(board[i][j] == word.charAt(0)) {
if(dfs(board, word, i, j, 0)) {
return true;
}
}
}
}
// 所有起点尝试完毕,没有找到合法路径
return false;
}
boolean dfs(char[][] board, String word, int r, int c, int idx) {
// 单词全部匹配成功
if(idx == word.length()) return true;
// 越界或者字符不匹配
if(r < 0 || r >= board.length || c < 0 || c >= board[0].length || board[r][c] != word.charAt(idx)) {
return false;
}
// 标记已访问
char temp = board[r][c];
board[r][c] = '#';
// 四个方向搜索,短路或:找到一条通路就停止后续方向
boolean res =
dfs(board, word, r - 1, c, idx + 1) ||
dfs(board, word, r + 1, c, idx + 1) ||
dfs(board, word, r, c - 1, idx + 1) ||
dfs(board, word, r, c + 1, idx + 1);
// 回溯撤销标记,恢复原来字符
board[r][c] = temp;
return res;
}
}
三、实例演示(样例:board = [['A','B','C','E'],['S','F','C','S'],['A','D','E','E']],word = "ABCCED")
| 步骤 | r | c | idx | boardrc | 动作 |
|---|---|---|---|---|---|
| 1 | 0 | 0 | 0 | A | 匹配 A,暂存 A,改成 #,递归传入 idx=0,下一层 idx 变为 1 |
| 2 | 0 | 1 | 1 | B | 匹配 B,暂存 B,改成 #,下一层 idx 变为 2 |
| 3 | 0 | 2 | 2 | C | 匹配 C,暂存 C,改成 #,下一层 idx 变为 3 |
| 4 | 1 | 2 | 3 | C | 匹配 C,暂存 C,改成 #,下一层 idx 变为 4 |
| 5 | 2 | 2 | 4 | E | 匹配 E,暂存 E,改成 #,下一层 idx 变为 5 |
| 6 | 2 | 1 | 5 | D | 匹配 D,idx+1=6 等于单词长度,返回 true |
| 7 | 逐层回溯 | - | - | - | 每一层递归返回,依次把#还原成原始字符(撤销标记) |
关键点:idx 不会在本层自增,是调用递归的时候,传给下一层 idx+1,外层只负责传入初始 idx=0 作为起点。
四、复杂度分析
- 时间复杂度:(O(m * n * 4^L)),m、n 为网格行列数,L 是单词长度;每个起点最多 4 个方向分支;
- 空间复杂度:(O(L)),递归栈最大深度等于单词长度 L,原地修改写法不需要额外的 visited 数组。
五、易错点总结
- 外层
exist循环结束后必须写return false;,否则编译报错missing return statement; - 越界判断条件是
>=,不能写>,数组下标范围是0 ~ length-1; - 回溯撤销标记不能省略,忘记恢复字符会干扰其他搜索分支;
||是短路特性,找到合法路径之后,剩下方向不会递归,自带剪枝;- 原地修改会改变输入数组,如果题目不允许修改原数据,改用
boolean[][] visited标记访问。
六、个人感悟
网格回溯模板题,和之前组合总和、括号生成的回溯框架一致:选择 → 递归深入 → 回溯撤销。区别在于选择分支不是遍历数组,而是上下左右 4 个方向。和岛屿数量的 DFS 思想相通,岛屿是标记全部连通区域,本题是沿着一条路径逐个匹配字符,多了一个单词下标 idx 控制匹配进度。