LeetCode 79. 单词搜索(DFS 回溯法)

一、核心思路

在二维字符网格中寻找一条路径,可以按顺序拼出目标单词。行走规则:仅能上下左右相邻移动,同一个单元格不能在同一条路径重复使用。

  1. 遍历网格每一个格子,如果当前格子字符等于单词首字符,以此位置作为起点启动 DFS;

  2. DFS 参数:当前坐标r行、c列,idx表示当前需要匹配单词的第idx位字符;

  3. 终止条件:

    • idx == word.length():单词全部匹配完成,返回true;
    • 坐标越界,或者当前格子字符和word[idx]不相等:此路径不通,返回false;
  4. 回溯标记:原地将当前格子修改为#,标记为本轮路径已访问;递归返回后恢复原始字符,撤销标记,不影响其他分支搜索;

  5. 四个方向搜索:上、下、左、右,使用||短路或,只要任意一个方向返回 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 数组。

五、易错点总结

  1. 外层exist循环结束后必须写return false;,否则编译报错missing return statement;
  2. 越界判断条件是>=,不能写>,数组下标范围是0 ~ length-1;
  3. 回溯撤销标记不能省略,忘记恢复字符会干扰其他搜索分支;
  4. ||是短路特性,找到合法路径之后,剩下方向不会递归,自带剪枝;
  5. 原地修改会改变输入数组,如果题目不允许修改原数据,改用boolean[][] visited标记访问。

六、个人感悟

网格回溯模板题,和之前组合总和、括号生成的回溯框架一致:选择 → 递归深入 → 回溯撤销。区别在于选择分支不是遍历数组,而是上下左右 4 个方向。和岛屿数量的 DFS 思想相通,岛屿是标记全部连通区域,本题是沿着一条路径逐个匹配字符,多了一个单词下标 idx 控制匹配进度。

相关推荐
INGNIGHT4 小时前
440 · 背包问题(三)(无限次数)
算法·leetcode·动态规划
Tisfy6 小时前
LeetCode 3619.总价值可以被 K 整除的岛屿数目:广度优先搜索BFS
算法·leetcode·bfs·宽度优先
文丑颜不良啊9 小时前
LeetCode926~950题目解析
java·leetcode
alphaTao10 小时前
LeetCode 每日一题 2026/10/19-2026/10/25
算法·leetcode
Rain的Java大神之路11 小时前
面了 50 个候选人,90% 都挂在 LeetCode 3 无重复字符的最长子串:滑动窗口的 5 个致命坑
java·后端·leetcode
文丑颜不良啊1 天前
LeetCode876~900题目解析
java·leetcode
木井巳1 天前
【动态规划: 路径问题】不同路径
java·算法·leetcode·动态规划
晒太羊的猫1 天前
hot100子串
java·算法·leetcode
海清河晏1111 天前
加油站问题
数据结构·算法·leetcode