docs: improve solution explanations for remaining 3 LeetCode problems
- 电话号码的字母组合: 添加思路推导、详细流程、执行演示和常见错误 - 子集: 添加思路推导、详细流程、执行演示和常见错误 - 单词搜索: 完全重写,添加完整的思路推导、详细流程、边界分析等 所有文件现在都包含: - 思路推导(从暴力解法分析) - 详细的算法流程(含Q&A) - 关键细节说明 - 边界条件分析 - 执行过程演示 - 常见错误分析
This commit is contained in:
@@ -26,20 +26,314 @@
|
|||||||
输出:false
|
输出:false
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 约束条件
|
||||||
|
|
||||||
|
- `m == board.length`
|
||||||
|
- `n == board[i].length`
|
||||||
|
- `1 <= m, n <= 6`
|
||||||
|
- `1 <= word.length <= 15`
|
||||||
|
- `board` 和 `word` 仅由大小写英文字母组成
|
||||||
|
|
||||||
|
## 思路推导
|
||||||
|
|
||||||
|
### 暴力解法分析
|
||||||
|
|
||||||
|
**第一步:最直观的思路 - 从每个位置开始搜索**
|
||||||
|
|
||||||
|
```python
|
||||||
|
def exist_brute(board, word):
|
||||||
|
if not board or not word:
|
||||||
|
return False
|
||||||
|
|
||||||
|
m, n = len(board), len(board[0])
|
||||||
|
|
||||||
|
# 从每个位置开始尝试
|
||||||
|
for i in range(m):
|
||||||
|
for j in range(n):
|
||||||
|
if board[i][j] == word[0]: # 找到起始位置
|
||||||
|
if search_from(board, i, j, word, 0):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def search_from(board, i, j, word, index):
|
||||||
|
# 从 (i,j) 位置开始搜索 word[index:]
|
||||||
|
if index == len(word):
|
||||||
|
return True
|
||||||
|
|
||||||
|
if not is_valid(board, i, j):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if board[i][j] != word[index]:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 标记访问
|
||||||
|
temp = board[i][j]
|
||||||
|
board[i][j] = '#'
|
||||||
|
|
||||||
|
# 向四个方向搜索
|
||||||
|
found = (search_from(board, i+1, j, word, index+1) or
|
||||||
|
search_from(board, i-1, j, word, index+1) or
|
||||||
|
search_from(board, i, j+1, word, index+1) or
|
||||||
|
search_from(board, i, j-1, word, index+1))
|
||||||
|
|
||||||
|
# 回溯
|
||||||
|
board[i][j] = temp
|
||||||
|
|
||||||
|
return found
|
||||||
|
```
|
||||||
|
|
||||||
|
**时间复杂度分析:**
|
||||||
|
- 最坏情况:从每个位置开始搜索
|
||||||
|
- 每次搜索最多访问 m×n 个格子
|
||||||
|
- 每个格子有 4 个方向
|
||||||
|
- **总时间复杂度:O(m × n × 4^k)**,其中 k 是单词长度
|
||||||
|
|
||||||
|
**问题:**
|
||||||
|
- 这个算法已经是最优的了!
|
||||||
|
- 但可以通过剪枝优化
|
||||||
|
|
||||||
|
### 优化思考 - 如何减少不必要的搜索?
|
||||||
|
|
||||||
|
**核心观察:**
|
||||||
|
1. **提前终止**:如果单词长度超过格子数,直接返回 false
|
||||||
|
2. **字符频率检查**:如果 board 中某个字符数量不足,直接返回 false
|
||||||
|
3. **搜索顺序优化**:从稀有的字符开始搜索
|
||||||
|
|
||||||
|
**为什么用 DFS + 回溯?**
|
||||||
|
- 需要遍历所有可能的路径
|
||||||
|
- 每个位置只能访问一次(需要标记)
|
||||||
|
- 找到一条有效路径即可返回
|
||||||
|
|
||||||
|
### 为什么这样思考?
|
||||||
|
|
||||||
|
**1. 路径搜索视角**
|
||||||
|
```
|
||||||
|
board = [["A","B","C","E"],
|
||||||
|
["S","F","C","S"],
|
||||||
|
["A","D","E","E"]]
|
||||||
|
word = "ABCCED"
|
||||||
|
|
||||||
|
搜索过程:
|
||||||
|
(0,0)A → (0,1)B → (0,2)C → (1,2)C → (1,3)E → (0,3)D
|
||||||
|
✓ 找到完整路径
|
||||||
|
|
||||||
|
关键点:
|
||||||
|
- 每次只能向上下左右移动
|
||||||
|
- 不能重复使用同一个格子
|
||||||
|
- 找到一条路径即可返回 true
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. 回溯的必要性**
|
||||||
|
```
|
||||||
|
为什么不直接 DFS?
|
||||||
|
- 需要标记已访问的格子
|
||||||
|
- 如果一条路径不通,需要回溯并尝试其他方向
|
||||||
|
- 必须恢复原始状态(撤销标记)
|
||||||
|
|
||||||
|
回溯 = DFS + 状态恢复
|
||||||
|
```
|
||||||
|
|
||||||
## 解题思路
|
## 解题思路
|
||||||
|
|
||||||
### 方法一:DFS + 回溯(推荐)
|
### 方法一:DFS + 回溯(推荐)
|
||||||
|
|
||||||
**核心思想:**对每个位置进行 DFS,搜索是否存在匹配的单词路径。
|
**核心思想:**对每个位置进行 DFS,搜索是否存在匹配的单词路径。
|
||||||
|
|
||||||
**算法步骤:**
|
### 详细算法流程
|
||||||
1. 遍历网格的每个位置
|
|
||||||
2. 如果当前位置字符匹配单词首字符,开始 DFS
|
**步骤1:遍历所有可能的起始位置**
|
||||||
3. DFS 过程中:
|
|
||||||
- 标记当前已访问
|
```python
|
||||||
- 向四个方向递归搜索
|
for i in range(m):
|
||||||
- 如果找到完整单词,返回 true
|
for j in range(n):
|
||||||
- 回溯时撤销访问标记
|
if board[i][j] == word[0]: # 首字符匹配
|
||||||
|
if dfs(i, j, 0):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
```
|
||||||
|
|
||||||
|
**Q: 为什么要从每个位置开始搜索?**
|
||||||
|
|
||||||
|
A: 因为单词可能从 board 的任意位置开始。举例:
|
||||||
|
```
|
||||||
|
board = [["A","B"],
|
||||||
|
["C","D"]]
|
||||||
|
word = "BD"
|
||||||
|
|
||||||
|
必须从 (0,1) 的 'B' 开始搜索
|
||||||
|
```
|
||||||
|
|
||||||
|
**步骤2:设计 DFS 函数**
|
||||||
|
|
||||||
|
```python
|
||||||
|
def dfs(i, j, k):
|
||||||
|
# k 表示当前匹配到 word 的第几个字符
|
||||||
|
|
||||||
|
# 终止条件:找到完整单词
|
||||||
|
if k == len(word):
|
||||||
|
return True
|
||||||
|
|
||||||
|
# 边界检查
|
||||||
|
if i < 0 or i >= m or j < 0 or j >= n:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 已访问检查
|
||||||
|
if visited[i][j]:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 字符匹配检查
|
||||||
|
if board[i][j] != word[k]:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 标记访问
|
||||||
|
visited[i][j] = True
|
||||||
|
|
||||||
|
# 向四个方向搜索
|
||||||
|
found = dfs(i+1, j, k+1) or dfs(i-1, j, k+1) or \
|
||||||
|
dfs(i, j+1, k+1) or dfs(i, j-1, k+1)
|
||||||
|
|
||||||
|
# 回溯:取消标记
|
||||||
|
visited[i][j] = False
|
||||||
|
|
||||||
|
return found
|
||||||
|
```
|
||||||
|
|
||||||
|
**Q: 为什么边界检查放在字符匹配之前?**
|
||||||
|
|
||||||
|
A: 为了避免数组越界错误。顺序很重要:
|
||||||
|
1. 先检查边界(防止越界)
|
||||||
|
2. 再检查是否已访问
|
||||||
|
3. 最后检查字符是否匹配
|
||||||
|
|
||||||
|
**Q: 为什么用 or 连接四个方向的搜索?**
|
||||||
|
|
||||||
|
A: 因为只要有一个方向找到完整单词即可返回 true。
|
||||||
|
|
||||||
|
**步骤3:优化 - 提前检查字符频率**
|
||||||
|
|
||||||
|
```python
|
||||||
|
from collections import Counter
|
||||||
|
|
||||||
|
def exist_optimized(board, word):
|
||||||
|
# 检查字符频率
|
||||||
|
board_chars = Counter(c for row in board for c in row)
|
||||||
|
word_chars = Counter(word)
|
||||||
|
|
||||||
|
for char, count in word_chars.items():
|
||||||
|
if board_chars[char] < count:
|
||||||
|
return False # board 中该字符不足
|
||||||
|
|
||||||
|
# ... 后续搜索逻辑
|
||||||
|
```
|
||||||
|
|
||||||
|
### 关键细节说明
|
||||||
|
|
||||||
|
**细节1:为什么需要 visited 数组?**
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 没有 visited 的情况
|
||||||
|
board = [["A","A"],
|
||||||
|
["A","A"]]
|
||||||
|
word = "AAAA"
|
||||||
|
|
||||||
|
如果不标记已访问:
|
||||||
|
- (0,0)A → (0,1)A → (0,0)A → (0,1)A → ...
|
||||||
|
- 会无限循环,重复访问同一个格子
|
||||||
|
|
||||||
|
使用 visited:
|
||||||
|
- (0,0)A → (0,1)A → (1,1)A → (1,0)A ✓
|
||||||
|
- 每个格子只访问一次
|
||||||
|
```
|
||||||
|
|
||||||
|
**细节2:为什么找到完整单词后立即返回?**
|
||||||
|
|
||||||
|
```python
|
||||||
|
if k == len(word):
|
||||||
|
return True # 立即返回,不继续搜索
|
||||||
|
```
|
||||||
|
|
||||||
|
**为什么?**
|
||||||
|
- 题目只要求判断是否存在,不要求找到所有路径
|
||||||
|
- 找到一条路径即可返回,节省时间
|
||||||
|
|
||||||
|
**细节3:为什么需要回溯(撤销标记)?**
|
||||||
|
|
||||||
|
```python
|
||||||
|
visited[i][j] = True # 标记
|
||||||
|
# ... 搜索
|
||||||
|
visited[i][j] = False # 撤销
|
||||||
|
```
|
||||||
|
|
||||||
|
**为什么必须撤销?**
|
||||||
|
- 因为其他搜索路径可能需要经过这个格子
|
||||||
|
- 举例:
|
||||||
|
```
|
||||||
|
board = [["A","B"],
|
||||||
|
["C","D"]]
|
||||||
|
word = "ABDC"
|
||||||
|
|
||||||
|
路径1:(0,0)A → (0,1)B → (1,1)D → (1,0)C ✓
|
||||||
|
路径2:(0,0)A → (1,0)C → (1,1)D → (0,1)B ✓
|
||||||
|
|
||||||
|
如果不撤销:
|
||||||
|
- 路径1 访问了所有格子后,所有格子都标记为已访问
|
||||||
|
- 路径2 无法再搜索(虽然路径2也是有效的)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 边界条件分析
|
||||||
|
|
||||||
|
**边界1:单词长度为 1**
|
||||||
|
```
|
||||||
|
输入:board = [["A"]], word = "A"
|
||||||
|
输出:true
|
||||||
|
过程:直接检查 board[0][0] == 'A'
|
||||||
|
```
|
||||||
|
|
||||||
|
**边界2:单词不存在**
|
||||||
|
```
|
||||||
|
输入:board = [["A","B"],["C","D"]], word = "ABCE"
|
||||||
|
输出:false
|
||||||
|
原因:没有 'E' 字符
|
||||||
|
```
|
||||||
|
|
||||||
|
**边界3:所有格子都相同**
|
||||||
|
```
|
||||||
|
输入:board = [["A","A"],["A","A"]], word = "AAAA"
|
||||||
|
输出:true
|
||||||
|
注意:需要确保不重复访问同一个格子
|
||||||
|
```
|
||||||
|
|
||||||
|
**边界4:单词长度超过格子数**
|
||||||
|
```
|
||||||
|
输入:board = [["A","B"]], word = "ABC"
|
||||||
|
输出:false
|
||||||
|
优化:可以在开始前检查 len(word) > m×n
|
||||||
|
```
|
||||||
|
|
||||||
|
### 复杂度分析(详细版)
|
||||||
|
|
||||||
|
**时间复杂度:**
|
||||||
|
```
|
||||||
|
- 外层循环:遍历所有位置 - O(m×n)
|
||||||
|
- 内层 DFS:最坏情况访问所有格子 - O(m×n×4^k)
|
||||||
|
|
||||||
|
为什么是 4^k?
|
||||||
|
- 每个位置有 4 个方向
|
||||||
|
- 单词长度为 k
|
||||||
|
- 最坏情况需要搜索 4^k 条路径
|
||||||
|
|
||||||
|
**总时间复杂度:O(m×n×4^k)**
|
||||||
|
|
||||||
|
实际运行中,由于边界和字符匹配的检查,实际复杂度会低很多。
|
||||||
|
```
|
||||||
|
|
||||||
|
**空间复杂度:**
|
||||||
|
```
|
||||||
|
- visited 数组:O(m×n)
|
||||||
|
- 递归栈深度:O(k)(单词长度)
|
||||||
|
- **总空间复杂度:O(m×n)**
|
||||||
|
```
|
||||||
|
|
||||||
## 代码实现
|
## 代码实现
|
||||||
|
|
||||||
@@ -95,20 +389,278 @@ func exist(board [][]byte, word string) bool {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**LeetCode 212:** 给定一个 m x n 二维字符网格 board 和一个单词列表 words,返回所有在二维网格和字典中出现的单词。
|
## 执行过程演示
|
||||||
|
|
||||||
|
以 `board = [["A","B","C"],["S","F","C"],["A","D","E"]], word = "ABCCED"` 为例:
|
||||||
|
|
||||||
|
```
|
||||||
|
初始状态:visited 全为 false
|
||||||
|
|
||||||
|
从 (0,0) 开始,board[0][0] = 'A' == word[0]
|
||||||
|
|
||||||
|
DFS(0, 0, 0):
|
||||||
|
board[0][0] = 'A' == word[0] ✓
|
||||||
|
visited[0][0] = true
|
||||||
|
|
||||||
|
DFS(1, 0, 1): // 向下
|
||||||
|
board[1][0] = 'S' != 'B' ✗
|
||||||
|
|
||||||
|
DFS(-1, 0, 1): // 向上
|
||||||
|
越界 ✗
|
||||||
|
|
||||||
|
DFS(0, 1, 1): // 向右
|
||||||
|
board[0][1] = 'B' == word[1] ✓
|
||||||
|
visited[0][1] = true
|
||||||
|
|
||||||
|
DFS(1, 1, 2): // 向下
|
||||||
|
board[1][1] = 'F' != 'C' ✗
|
||||||
|
|
||||||
|
DFS(-1, 1, 2): // 向上
|
||||||
|
越界 ✗
|
||||||
|
|
||||||
|
DFS(0, 2, 2): // 向右
|
||||||
|
board[0][2] = 'C' == word[2] ✓
|
||||||
|
visited[0][2] = true
|
||||||
|
|
||||||
|
DFS(1, 2, 3): // 向下
|
||||||
|
board[1][2] = 'C' == word[3] ✓
|
||||||
|
visited[1][2] = true
|
||||||
|
|
||||||
|
DFS(2, 2, 4): // 向下
|
||||||
|
board[2][2] = 'E' != word[4]='E' 实际是相等的 ✓
|
||||||
|
visited[2][2] = true
|
||||||
|
|
||||||
|
DFS(3, 2, 5): // 向下
|
||||||
|
越界 ✗
|
||||||
|
|
||||||
|
DFS(1, 2, 5): // 向上
|
||||||
|
visited[1][2] = true ✗ 已访问
|
||||||
|
|
||||||
|
DFS(2, 3, 5): // 向右
|
||||||
|
越界 ✗
|
||||||
|
|
||||||
|
DFS(2, 1, 5): // 向左
|
||||||
|
board[2][1] = 'D' == word[5] ✓
|
||||||
|
DFS(2, 1, 6): // k=6 == len(word),返回 true!
|
||||||
|
|
||||||
|
返回 true
|
||||||
|
```
|
||||||
|
|
||||||
|
## 常见错误
|
||||||
|
|
||||||
|
### 错误1:忘记撤销访问标记
|
||||||
|
|
||||||
|
❌ **错误写法:**
|
||||||
|
```go
|
||||||
|
visited[i][j] = true
|
||||||
|
found := dfs(i+1, j, k+1) || dfs(i-1, j, k+1) || ...
|
||||||
|
// 忘记撤销
|
||||||
|
return found
|
||||||
|
```
|
||||||
|
|
||||||
|
✅ **正确写法:**
|
||||||
|
```go
|
||||||
|
visited[i][j] = true
|
||||||
|
found := dfs(i+1, j, k+1) || dfs(i-1, j, k+1) || ...
|
||||||
|
visited[i][j] = false // 必须撤销
|
||||||
|
return found
|
||||||
|
```
|
||||||
|
|
||||||
|
**原因:**不撤销会导致其他路径无法访问该格子。
|
||||||
|
|
||||||
|
### 错误2:边界检查顺序错误
|
||||||
|
|
||||||
|
❌ **错误写法:**
|
||||||
|
```go
|
||||||
|
if board[i][j] != word[k] || // 先检查字符,可能越界!
|
||||||
|
i < 0 || i >= m || j < 0 || j >= n {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
✅ **正确写法:**
|
||||||
|
```go
|
||||||
|
if i < 0 || i >= m || j < 0 || j >= n || // 先检查边界
|
||||||
|
visited[i][j] || board[i][j] != word[k] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**原因:**必须先检查边界,否则会数组越界。
|
||||||
|
|
||||||
|
### 错误3:直接修改 board 而不使用 visited
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func findWords(board [][]byte, words []string) []string {
|
// 可以这样做,但需要恢复
|
||||||
// 构建 Trie 树
|
temp := board[i][j]
|
||||||
trie := buildTrie(words)
|
board[i][j] = '#'
|
||||||
result := []string{}
|
// ... 搜索
|
||||||
|
board[i][j] = temp // 必须恢复
|
||||||
|
```
|
||||||
|
|
||||||
for i := 0; i < len(board); i++ {
|
**问题:**如果忘记恢复,会导致错误。使用 visited 数组更安全。
|
||||||
for j := 0; j < len(board[0]); j++ {
|
|
||||||
dfsBoard(board, i, j, trie, &result)
|
## 进阶问题
|
||||||
|
|
||||||
|
### Q1: 如何找到所有可能的路径?
|
||||||
|
|
||||||
|
**A:** 不在找到第一条路径时立即返回,而是继续搜索。
|
||||||
|
|
||||||
|
```go
|
||||||
|
func findAllPaths(board [][]byte, word string) [][]string {
|
||||||
|
m, n := len(board), len(board[0])
|
||||||
|
visited := make([][]bool, m)
|
||||||
|
for i := range visited {
|
||||||
|
visited[i] = make([]bool, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := [][]string{}
|
||||||
|
|
||||||
|
var dfs func(i, j, k int, path []string)
|
||||||
|
dfs = func(i, j, k int, path []string) {
|
||||||
|
if k == len(word) {
|
||||||
|
temp := make([]string, len(path))
|
||||||
|
copy(temp, path)
|
||||||
|
result = append(result, temp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if i < 0 || i >= m || j < 0 || j >= n ||
|
||||||
|
visited[i][j] || board[i][j] != word[k] {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
visited[i][j] = true
|
||||||
|
path = append(path, fmt.Sprintf("(%d,%d)", i, j))
|
||||||
|
|
||||||
|
dfs(i+1, j, k+1, path)
|
||||||
|
dfs(i-1, j, k+1, path)
|
||||||
|
dfs(i, j+1, k+1, path)
|
||||||
|
dfs(i, j-1, k+1, path)
|
||||||
|
|
||||||
|
visited[i][j] = false
|
||||||
|
path = path[:len(path)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < m; i++ {
|
||||||
|
for j := 0; j < n; j++ {
|
||||||
|
if board[i][j] == word[0] {
|
||||||
|
dfs(i, j, 0, []string{})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Q2: 如何优化搜索顺序?
|
||||||
|
|
||||||
|
**A:** 从单词中稀有的字符开始搜索。
|
||||||
|
|
||||||
|
```go
|
||||||
|
func existOptimized(board [][]byte, word string) bool {
|
||||||
|
// 统计 board 中字符频率
|
||||||
|
charCount := make(map[byte]int)
|
||||||
|
for i := range board {
|
||||||
|
for j := range board[i] {
|
||||||
|
charCount[board[i][j]]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有字符不足
|
||||||
|
wordCount := make(map[byte]int)
|
||||||
|
for i := range word {
|
||||||
|
wordCount[word[i]]++
|
||||||
|
}
|
||||||
|
|
||||||
|
for char, count := range wordCount {
|
||||||
|
if charCount[char] < count {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... 后续搜索逻辑
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q3: 单词搜索 II - 如何搜索多个单词?
|
||||||
|
|
||||||
|
**A:** 使用 Trie 树优化。
|
||||||
|
|
||||||
|
```go
|
||||||
|
type TrieNode struct {
|
||||||
|
children [26]*TrieNode
|
||||||
|
word string
|
||||||
|
}
|
||||||
|
|
||||||
|
func findWords(board [][]byte, words []string) []string {
|
||||||
|
// 构建 Trie 树
|
||||||
|
root := buildTrie(words)
|
||||||
|
result := []string{}
|
||||||
|
|
||||||
|
var dfs func(i, j, node *TrieNode)
|
||||||
|
dfs = func(i, j, node *TrieNode) {
|
||||||
|
c := board[i][j]
|
||||||
|
if c == '#' || node.children[c-'A'] == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
node = node.children[c-'A']
|
||||||
|
if node.word != "" {
|
||||||
|
result = append(result, node.word)
|
||||||
|
node.word = "" // 避免重复添加
|
||||||
|
}
|
||||||
|
|
||||||
|
board[i][j] = '#' // 标记访问
|
||||||
|
if i > 0 {
|
||||||
|
dfs(i-1, j, node)
|
||||||
|
}
|
||||||
|
if i < len(board)-1 {
|
||||||
|
dfs(i+1, j, node)
|
||||||
|
}
|
||||||
|
if j > 0 {
|
||||||
|
dfs(i, j-1, node)
|
||||||
|
}
|
||||||
|
if j < len(board[0])-1 {
|
||||||
|
dfs(i, j+1, node)
|
||||||
|
}
|
||||||
|
board[i][j] = c // 恢复
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range board {
|
||||||
|
for j := range board[i] {
|
||||||
|
dfs(i, j, root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## P7 加分项
|
||||||
|
|
||||||
|
### 1. 相关题目推荐
|
||||||
|
|
||||||
|
- LeetCode 79: 单词搜索(本题)
|
||||||
|
- LeetCode 212: 单词搜索 II
|
||||||
|
- LeetCode 212 需要用 Trie 树优化
|
||||||
|
|
||||||
|
### 2. 实际应用场景
|
||||||
|
|
||||||
|
- **填字游戏**:判断单词是否可以由给定字母组成
|
||||||
|
- **Boggle 游戏**:在字母网格中找出所有有效单词
|
||||||
|
- **DNA 序列匹配**:在基因序列中查找特定模式
|
||||||
|
- **路径规划**:在迷宫中寻找特定路径
|
||||||
|
|
||||||
|
### 3. 面试技巧
|
||||||
|
|
||||||
|
**面试官可能会问:**
|
||||||
|
1. "为什么用 DFS 而不是 BFS?"
|
||||||
|
2. "如何优化搜索效率?"
|
||||||
|
3. "如何处理大量单词的搜索?"
|
||||||
|
|
||||||
|
**回答要点:**
|
||||||
|
1. DFS 更适合路径搜索,自然表达递归关系
|
||||||
|
2. 可以通过字符频率检查、搜索顺序优化等方式
|
||||||
|
3. 使用 Trie 树可以共享前缀,提高效率
|
||||||
|
|||||||
Reference in New Issue
Block a user