征服 LeetCode 79:单词搜索的巧妙解法
2023-10-23 03:05:57
征服 LeetCode 79:破解单词搜索谜团
简介
LeetCode 79:单词搜索是算法爱好者中颇具挑战的一道题,考验着解决复杂字符串匹配问题的技能。作为算法之旅中的一大障碍,这道题让初学者望而生畏。但是,掌握回溯算法的精髓,你将能轻松解开这个谜团。
回溯算法:分而治之的利器
单词搜索本质上是一个匹配问题。在一个二维网格中,你需要找出给定的目标单词是否存在。暴力枚举法虽然简单,但效率低下,尤其是在网格庞大的情况下。
回溯算法提供了一种更优雅的解决方案。它通过分而治之的方法,逐层深入网格,探索不同的搜索路径。当发现当前路径不可行,算法回溯到上一步,尝试不同的路径。
JavaScript 实现:一步一步征服
用 JavaScript 实现单词搜索算法非常简单。我们定义一个函数 searchWord
,接受两个参数:board
(网格)和 word
(目标单词)。
function searchWord(board, word) {
// ... 算法实现
}
递归回溯:探索所有可能性
searchWord
函数通过递归调用实现回溯。在每层,算法检查当前单元格是否与目标单词的第一个字母匹配。如果是,继续递归探索相邻单元格,检查下一个字母。当发现路径不可行,算法回溯,尝试不同路径。
递归终止条件:成功或失败
递归终止有两种情况:
- 找到单词: 遍历完目标单词所有字母,且全部匹配时,成功找到单词,返回
true
。 - 遍历所有路径: 遍历完网格所有单元格,未找到单词时,返回
false
。
代码实现:清晰简洁
function searchWord(board, word) {
const m = board.length;
const n = board[0].length;
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
if (board[i][j] === word[0]) {
if (dfs(i, j, 0)) {
return true;
}
}
}
}
return false;
}
function dfs(row, col, index) {
if (index === word.length - 1) {
return true;
}
if (row < 0 || row >= m || col < 0 || col >= n || board[row][col] !== word[index]) {
return false;
}
// 标记当前单元格已访问
const temp = board[row][col];
board[row][col] = '*';
const found =
dfs(row + 1, col, index + 1) ||
dfs(row - 1, col, index + 1) ||
dfs(row, col + 1, index + 1) ||
dfs(row, col - 1, index + 1);
// 恢复当前单元格的值
board[row][col] = temp;
return found;
}
复杂度分析:高效解决大规模问题
单词搜索算法的时间复杂度为 O(mn4^wordLength),其中 m 和 n 是网格的行数和列数,wordLength 是目标单词的长度。这种复杂度是因为算法对每个网格单元格进行四次递归调用(向上、下、左、右)。
常见问题解答
-
回溯算法的优势是什么?
回溯算法允许探索所有可能的搜索路径,并回溯到以前的状态,有效解决组合问题。 -
为什么暴力枚举法效率低下?
暴力枚举法逐个检查所有可能的路径,当网格很大时,搜索空间变得庞大,导致效率低下。 -
回溯算法如何避免重复探索?
算法通过标记访问过的单元格来避免重复探索,确保只探索未访问的路径。 -
算法如何处理目标单词不存在的情况?
算法遍历完网格所有单元格,如果未找到匹配的单词,则返回false
。 -
回溯算法的其他应用是什么?
回溯算法广泛用于解决组合问题,如八皇后问题、数独问题和排列组合问题。