给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例 1:
输入: board = [['A','B','C','E'],['S','F','C','S'],['A','D','E','E']], word = "ABCCED"
输出: true
示例 2:
输入: board = [['A','B','C','E'],['S','F','C','S'],['A','D','E','E']], word = "SEE"
输出: true
示例 3:
输入: board = [['A','B','C','E'],['S','F','C','S'],['A','D','E','E']], word = "ABCB"
输出: false
提示:
m == board.lengthn = board[i].length1 <= m, n <= 61 <= word.length <= 15board和word仅由大小写英文字母组成
1. 生活案例:特工在迷宫中搜寻暗号
想象你是一名特工,被派往一个 的房间矩阵里寻找一份秘密文件(单词 word)。
-
规则:
- 你必须按照暗号字母的顺序走(先找第一个字母,再找相邻的第二个...)。
- 你只能向上、下、左、右相邻的房间移动。
- 关键约束:在同一次任务中,你不能重复进入同一个房间(走过的路要插个旗子标记,回来时再拔掉)。
-
过程:
- 你从矩阵的每一个房间开始尝试(外层双循环)。
- 进入房间,如果字母对上了,你就在脚下抹上油漆(标记为
'#'),然后去敲邻居的门。 - 如果四个邻居都带不向终点,说明这条路走错了,你得把脚下的油漆擦干净(还原字符),退回到上一个房间重新选路。
2. 代码实现与详细注释
这是你图片中的代码,我为你添加了详细的“特工行动”注释:
JavaScript
/**
* @param {character[][]} board - 字母矩阵
* @param {string} word - 目标单词
* @return {boolean}
*/
var exist = function (board, word) {
let m = board.length;
let n = board[0].length;
// 核心:深度优先搜索 (DFS)
function dfsSearch(i, j, len) {
// 【成功出口】:如果匹配的长度等于单词长度,说明全找到了
if (len == word.length) return true;
// 【失败出口】:
// 1. 越界(跑出矩阵了)
// 2. 当前房间字母不对,或者这个房间刚才已经走过了(标记为'#'的)
if (i < 0 || i >= m || j < 0 || j >= n || board[i][j] !== word[len]) {
return false;
}
// 【执行搜寻】:
// 1. 先把当前房间做个标记,防止这一趟任务里回头重复走
let temp = board[i][j];
board[i][j] = '#';
// 2. 派分身去四个方向探路
// 只要有一个分身说“找到了”,结果就是 true
let res = dfsSearch(i - 1, j, len + 1) || // 上
dfsSearch(i + 1, j, len + 1) || // 下
dfsSearch(i, j - 1, len + 1) || // 左
dfsSearch(i, j + 1, len + 1); // 右
// 3. 【回溯关键点】:擦除油漆,拔掉旗子
// 不管有没有找到,都要把房间还原,不能影响特工下一次从别的起点开始的搜寻
board[i][j] = temp;
return res;
}
// 从矩阵的每一个格子作为起点开始“地毯式搜索”
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
// 如果某一个起点出发能成功,直接收工返回 true
if (dfsSearch(i, j, 0)) return true;
}
}
return false; // 找遍了所有起点都无果
};
3. 核心原理解析
为什么必须回溯 board[i][j] = temp?
回溯是算法的“后悔药”。
假设你要找 "ABCCED",你在路径 A -> B -> C 走到了死胡同。如果你不把路径上的字母还原,当你从另一个起点(比如旁边的另一个 A)再次搜索路过这些格子时,它们会被误认为“已访问过”,从而导致漏掉正确答案。
为什么用 DFS 而不是 BFS?
- DFS(深度优先) :像特工潜入,一条路走到黑,不行再回头。它配合回溯非常节省内存,只需要记录当前的路径。
- BFS(广度优先) :像洪水暴发,同时向四周蔓延。在处理“是否存在某条路径”且包含“不能重复访问”逻辑时,BFS 会产生海量的状态记录,效率较低。
复杂度分析
- 时间复杂度:。其中 是矩阵大小, 是单词长度。每个格子都要尝试,每个字母有 3 个方向可走(除了来的那个方向)。
- 空间复杂度:。递归的最大深度就是单词的长度。