当前位置: 首页 > news >正文

Leetcode-100 回溯法-单词搜索

LeetCode 79: 单词搜索 — 深度优先搜索 (DFS) 回溯法

题目描述

给定一个 m x n 的二维字符网格 board 和一个字符串 word,如果 word 存在于网格中,返回 true;否则返回 false

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

问题分析

该问题可以通过 回溯法 (Backtracking) 来解决。核心思想是从网格的每个位置开始,递归地检查是否能拼出目标单词。每次搜索时,我们可以从当前位置往上下左右四个方向扩展,查找下一个字符。

问题挑战
  • 回溯:我们需要在搜索过程中逐步判断并退回,保证没有走重复路径。
  • 剪枝:我们需要判断某个路径是否是无效的,例如字符不匹配、越界等条件。

解题思路

  1. 遍历网格:首先遍历整个网格,找到与目标单词第一个字符匹配的位置。然后从该位置开始,递归地进行深度优先搜索 (DFS),并尝试拼出整个单词。
  2. 回溯搜索:在每次递归调用中,判断当前字符是否与目标单词的字符匹配。如果匹配,就继续向下一个字符移动。如果完全拼出目标单词,则返回 True。在递归过程中使用 visited 数组避免重复使用相同的网格格子。
  3. 剪枝:当发现字符不匹配或超出边界时,提前终止搜索。

算法步骤

  1. 初始化:获取网格的行数和列数,以及一个 visited 数组,用来标记已经访问过的格子。
  2. 递归函数:定义一个递归函数 backtrack(m, n, index),在 (m, n) 位置开始搜索目标单词的第 index 个字符。
    • 如果找到目标单词的所有字符,则返回 True
    • 如果当前字符匹配,则继续向四个方向(上、下、左、右)进行搜索。
    • 需要在递归前后标记格子的访问状态。
  3. 遍历网格:从网格的每一个位置出发,调用 backtrack 函数,判断是否能从这个位置找到单词。

完整代码实现

from typing import List

class Solution:
    def exist(self, board: List[List[str]], word: str) -> bool:
        # 获取网格的行列数
        if not board or not board[0]:  # 边界情况,网格为空
            return False

        row, col = len(board), len(board[0])
        # 创建访问标记数组,初始时所有格子都未访问
        visited = [[False] * col for _ in range(row)]  

        def backtrack(m, n, index):
            """回溯搜索 word[index:] 是否匹配"""
            # 如果找到完整的单词,返回 True
            if index == len(word):
                return True
            
            # 越界或字符不匹配,返回 False
            if m < 0 or m >= row or n < 0 or n >= col or visited[m][n] or board[m][n] != word[index]:
                return False  

            # 标记当前位置为已访问
            visited[m][n] = True

            # 递归搜索四个方向
            found = (backtrack(m - 1, n, index + 1) or  # 上
                     backtrack(m + 1, n, index + 1) or  # 下
                     backtrack(m, n - 1, index + 1) or  # 左
                     backtrack(m, n + 1, index + 1))    # 右

            # 回溯,恢复当前位置的状态
            visited[m][n] = False

            return found

        # 遍历整个网格,查找 word[0] 匹配的起点
        for i in range(row):
            for j in range(col):
                if board[i][j] == word[0] and backtrack(i, j, 0):
                    return True  # 一旦找到匹配,立即返回 True

        return False  # 如果遍历完所有位置都没找到匹配,返回 False

代码解释

  1. 初始化访问标记 visited

    • visited 是一个二维布尔数组,用来标记每个格子是否已经被访问。初始时,所有格子都设置为 False,表示没有访问过。
  2. backtrack 函数

    • 递归地搜索单词。如果当前字符匹配,则向四个方向继续递归。
    • 如果当前位置的字符与单词当前字符匹配,且不越界且未访问过,则继续搜索下一个字符。
    • 如果找到了完整的单词,则返回 True,否则继续回溯。
  3. 回溯操作

    • 在每次递归完成后,恢复 visited 数组的状态,允许其他路径探索该格子。
  4. 遍历整个网格

    • 从网格中的word[0]出发.

时间复杂度与空间复杂度

  • 时间复杂度:O(m * n * 4^L),其中 m 是网格的行数,n 是列数,L 是单词的长度。最坏情况下,我们需要对每个单元格进行 DFS 搜索,每个单元格最多可以向四个方向扩展 L 步。

  • 空间复杂度:O(m * n),由于 visited 数组的空间复杂度为 O(m * n),递归深度最大为单词的长度 L

相关文章:

  • c#中的virtual方法
  • redis错误分析 forceUnlock的问题说明
  • #基于Django实现机器学习医学指标概率预测网站
  • 双塔模型2之如何选择正确的正负样本
  • Matlab基础知识与常见操作【无痛入门】
  • GPT Workspace体验
  • # 基于 OpenCV 的选择题自动批改系统实现
  • 预测地震的方法,如何使用AI和量子传感器发挥作用?
  • 基于PySide6与CATIA API的装配体位置管理工具开发实践
  • 使用Python爬虫获取淘宝App商品详情
  • HTML5贪吃蛇游戏开发经验分享
  • 在DE2-115板子上用 Verilog编程实现一个 分秒计数器,并具备按键暂停、按键消抖功能
  • 拼多多 anti-token unidbg 分析
  • androidstudio安装完成后创建新的示例项目编译报错解决
  • VRRP交换机三层架构综合实验
  • Java 图书管理系统
  • 预览器的使用-查看多端设备预览效果
  • 人形机器人遥操作方式和作用
  • NAS这个名称的由来和翻译
  • 华为昇腾910B编程实战:大模型推理性能优化全攻略
  • 优化网站设计有哪些方法/xp优化大师
  • 建网站怎么做/百度品牌广告多少钱一个月
  • 网站建设用户登录/安卓手机优化大师官方下载
  • 陕西建设工程合同备案网站/教育机构
  • 百度网站收录查询/专业做网站设计
  • 深圳网站制作问/快速网站推广优化