79. 单词搜索(Word Search)

难度:中等 | 主题:回溯 + visited 标记

题目

给定一个 m x n 二维字符网格 board 和一个字符串单词 word。如果 word 存在于网格中,返回 true;否则返回 false。单词必须按照字母顺序,通过相邻的单元格内的字母构成。

示例

 
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
 
输出:true
 

思路

先讲个故事:寻宝游戏,每步只能走相邻格子

你拿到一张藏宝图(二维网格),要按顺序踩过字母拼出单词。

规则:

  • 从任意格子出发

  • 每次只能走上下左右相邻的格子

  • 踩过的格子不能再踩(visited 标记)

  • 找到完整单词就赢了


引导式推导:DFS 回溯 + 剪枝

核心流程:


flowchart TD

    A["遍历每个格子作为起点"] --> B["check(i, j, k)"]

    B --> C{"board[i][j] == word[k]?"}

    C -->|"否"| D["返回 False"]

    C -->|"是"| E{"k == len(word)-1?"}

    E -->|"是"| F["返回 True"]

    E -->|"否"| G["标记 visited"]

    G --> H["向四个方向递归 k+1"]

    H --> I["回溯: 取消 visited"]

    I --> J["返回 False"]

关键优化: 第一次 4 方向,之后每次 3 方向(不能往回走),所以是 3^L 而不是 4


代码

 
class Solution:
 
    def exist(self, board: list[list[str]], word: str) -> bool:
 
        h, w = len(board), len(board[0])
 
        visited = [[False] * w for _ in range(h)]
 
        def check(i: int, j: int, k: int) -> bool:
 
            if board[i][j] != word[k]:
 
                return False
 
            if k == len(word) - 1:
 
                return True
 
            visited[i][j] = True
 
            for di, dj in ((0,1), (0,-1), (1,0), (-1,0)):
 
                ni, nj = i + di, j + dj
 
                if 0 <= ni < h and 0 <= nj < w and not visited[ni][nj]:
 
                    if check(ni, nj, k + 1):
 
                        return True
 
            visited[i][j] = False
 
            return False
 
        return any(check(i, j, 0) for i in range(h) for j in range(w))
 

复杂度

指标解释
时间O(M × N × 3^L)M×N 个起点,每个 3^L 路径
空间O(M × N)visited 数组

实战考量

频率分析

出现在:字节/美团 回溯题,约 20% 常会考到。考察二维网格回溯和剪枝

延伸思考

Q:为什么 visited 要在递归前设为 True,递归后重置为 False?

A:同一条路径不能重复踩同一个格子,但不同路径之间互不影响。重置是回溯的核心——撤销选择,让其他路径可以走这个格子。

Q:时间复杂度为什么是 O(M × N × 3^L)?

A:M×N 个起点,每个起点最多探索 3^L 条路径(第一次 4 方向,之后每次 3 方向,因为不能往回走)。

Q:能不能不新建 visited 数组,原地修改 board?

A:可以:board[i][j] = '#' 标记已访问,回溯时还原原字符。省空间但需说明会修改输入。

Q:如果 word 很长怎么办?

A:3^L 指数增长,需要剪枝:统计 board 和 word 的字符频率,word 中某个字符比 board 中多就直接返回 False。

易错点

  • visited[i][j] 必须重置

  • 先判断当前字符匹配再判断 k == len(word)-1

  • 四方向索引不能越界


生活类比

寻宝游戏,踩过的格子不能再踩

拿着藏宝图,按顺序踩字母拼单词。

每踩一个格子就插个小旗(visited),表示”我来过”。

走到死胡同就拔掉小旗退回来(回溯),换条路试试。

插旗 → 走 → 拔旗退 → 换路——这就是二维回溯的标准动作。


相关题目

题目关系
200岛屿数量二维网格 DFS 基础
51N皇后二维回溯进阶
208实现Trie前缀树Trie 优化(单词搜索 II)
79单词搜索本题

→ 返回题单:LeetCode学习路线图 > 十、回溯

速记卡(面试闪卡)

Q1:一句话讲清「79. 单词搜索(Word Search)」到底是什么?

A:单词搜索是在二维字符网格中,沿上下左右相邻格子按顺序拼出目标单词的回溯题。

Q2:题目 —— 怎么理解?

A:像拿着藏宝图按顺序踩字母:从任意格子出发,每次只能走上下左右相邻格,踩过的格子插旗标记(visited)不能再踩,拼完整单词才算赢。Word Search(单词搜索)。

Q3:思路 —— 怎么理解?

A:像寻宝时插旗走迷宫:DFS 往四个方向试探,进入格子插旗(标记 visited),走不通就拔旗退回换路(回溯)。关键优化:第一次 4 方向、之后 3 方向,复杂度 3^L 而非 4^L。Depth-First Search(深度优先搜索)。

Q4:代码 —— 怎么理解?

A:像按固定套路探路:写 check(i,j,k) 递归,先判当前字母是否匹配,匹配则标记 visited、向四方向递归 k+1,返回前撤销标记(回溯)。越界与已访问都要兜住。Backtracking(回溯)。

Q5:复杂度 —— 怎么理解?

A:像评估探宝成本:时间 O(M×N×3^L)(M×N 个起点,每个最多 3^L 条路径),空间 O(M×N) 给 visited 数组。Time/Space Complexity(时间与空间复杂度)。

Q6:核心速记主线有哪些?

  • 题目:二维网格中按相邻字母顺序拼出目标单词

  • 思路:DFS + 回溯 + visited 标记,第一次 4 方向之后 3 方向

  • 代码:check 递归,匹配即标记、四方向试探、返回撤销

  • 复杂度:时间 O(M×N×3^L),空间 O(M×N)

口诀

A:网格踩字走相邻,插旗标记防重走

不通拔旗换条路,DFS 回溯是正途

首步四向后三步,三的 L 次幂记熟

时间 M N 乘三 L,空间网格一整张

相关链接