【LeetCode 热题 100】79. 单词搜索——回溯

Problem: 79. 单词搜索
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

文章目录

  • 整体思路
  • 完整代码
  • 时空复杂度
    • 时间复杂度:O(M * N * 3^L)
    • 空间复杂度:O(L)

整体思路

这段代码旨在解决一个经典的二维网格搜索问题:单词搜索 (Word Search)。其核心功能是判断一个给定的单词 word 是否存在于一个字符网格 board 中。构成单词的路径规则是:字母必须在网格中水平或垂直相邻,并且同一个单元格的字母在路径中不允许被重复使用。

该算法采用了 深度优先搜索(DFS) 结合 回溯(Backtracking) 的策略。其整体思路可以分解为以下几个步骤:

  1. 主驱动逻辑(遍历所有起点)

    • exist 方法是算法的入口。它通过两层嵌套的 for 循环遍历网格 board 中的每一个单元格 (i, j)
    • 这个遍历的目的是将每一个单元格都视作单词 word 的一个潜在 起始点
    • 对于每一个起点,它都会调用核心的 dfs 辅助函数来尝试进行深度搜索。如果任何一次 dfs 调用返回 true,意味着找到了完整的单词路径,程序立即返回 true
    • 如果遍历完所有可能的起点后,dfs 均未成功,则说明单词不存在于网格中,程序最后返回 false
  2. 核心搜索逻辑(DFS 与回溯)

    • dfs(i, j, k, ...) 是一个递归函数,负责从单元格 (i, j) 开始,尝试匹配 word 中从第 k 个字符开始的剩余部分。
    • 剪枝与基准情况
      • 失败剪枝:如果当前单元格 (i, j) 超出边界(在递归调用前检查),或者其字符 board[i][j] 与目标字符 word[k] 不匹配,则此路不通,直接返回 false
      • 成功基准:如果当前字符匹配,并且这已经是单词的最后一个字符(k == word.length - 1),则说明整个单词已经成功匹配,返回 true
    • 递归与回溯
      • 标记(Marking):为了防止在同一路径中重复使用单元格,在深入探索之前,代码会将当前单元格 board[i][j] 的值临时修改为一个特殊标记(如此代码中的 0)。这相当于一个“正在访问”的标记。
      • 探索(Exploration):接着,代码会向当前单元格的四个相邻方向(上、下、左、右)进行递归调用 dfs(x, y, k + 1, ...),尝试匹配单词的下一个字符。
      • 回溯(Backtracking):在四个方向的递归探索完成之后(无论成功还是失败),必须将当前单元格 board[i][j] 的值恢复为其原始字符 word[k]。这是回溯算法的精髓,它确保了在探索其他起始点的路径时,该单元格是可用的。
      • 如果四个方向的探索都没有找到完整的路径,则从当前点出发的搜索失败,返回 false

完整代码

class Solution {// 定义一个常量数组,用于表示四个方向的坐标偏移量:右、左、下、上private static final int[][] DIRECTION = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};/*** 在二维字符网格中查找是否存在一个给定的单词。* @param board 二维字符网格* @param word  要查找的单词* @return 如果单词存在,返回 true;否则,返回 false。*/public boolean exist(char[][] board, String word) {// 将目标单词转换为字符数组,以提高后续字符访问的效率char[] w = word.toCharArray();// 遍历网格中的每一个单元格,将其作为单词搜索的潜在起点for (int i = 0; i < board.length; i++) {for (int j = 0; j < board[0].length; j++) {// 从 (i, j) 开始进行深度优先搜索,尝试匹配单词的第一个字符 (k=0)if (dfs(i, j, 0, board, w)) {// 如果找到了一个完整的路径,立即返回 truereturn true;}}}// 如果遍历完所有可能的起点都未能找到单词,则返回 falsereturn false;}/*** 深度优先搜索辅助函数。* @param i      当前搜索的行索引* @param j      当前搜索的列索引* @param k      当前要匹配的目标单词中的字符索引* @param board  字符网格* @param word   目标单词的字符数组* @return 如果从 (i, j) 出发能找到 word[k...] 的路径,返回 true*/private boolean dfs(int i, int j, int k, char[][] board, char[] word) {// 剪枝:如果当前单元格的字符与目标字符不匹配,此路不通if (board[i][j] != word[k]) {return false;}// 成功基准:如果当前字符匹配,并且已是单词的最后一个字符,则搜索成功if (k == word.length - 1) {return true;}// 关键【标记】步骤:将当前单元格标记为已访问,防止在同一路径中重复使用。// 这里用一个非字符值(如0)来标记,也可以用一个不会在 board 和 word 中出现的特殊字符。board[i][j] = 0; // 探索四个相邻的方向for (int[] dir : DIRECTION) {int x = dir[0] + i;int y = dir[1] + j;// 检查新坐标 (x, y) 是否在网格边界内if (x >= 0 && x < board.length && y >= 0 && y < board[0].length) {// 对有效的相邻单元格进行递归调用,尝试匹配单词的下一个字符 (k+1)if (dfs(x, y, k + 1, board, word)) {// 如果任意方向的递归探索成功,立即返回 truereturn true;}}}// 关键【回溯】步骤:恢复当前单元格的原始值。// 这样,在回溯到上一个状态后,其他分支的搜索路径仍然可以使用此单元格。board[i][j] = word[k];// 如果所有方向都探索完毕,仍未找到匹配路径,则返回 falsereturn false;}
}

时空复杂度

时间复杂度:O(M * N * 3^L)

  1. 外层循环exist 方法中的两层 for 循环遍历了整个网格,这部分是 M * N 次操作,其中 M 是行数,N 是列数。
  2. DFS 复杂度:对于每个起点,都会调用 dfs 函数。在 dfs 函数中,每次递归都会向最多 3 个新的方向探索(因为不能走回头路,而修改 board 值的操作天然地防止了这一点)。
  3. 递归深度:递归的最大深度由单词的长度 L 决定。
  4. 综合分析
    • 在最坏的情况下,对于网格中的每个单元格 (M * N),我们都可能启动一次深度优先搜索。
    • 每次搜索的计算量大致可以估算为 3^L,因为每一步都有最多3个选择,持续 L 步。
    • 因此,总的时间复杂度是一个粗略的上限:O(M * N * 3^L),其中 L 是单词 word 的长度。

空间复杂度:O(L)

  1. 主要存储开销:算法的额外空间开销主要来自于递归调用栈。
  2. 递归深度dfs 函数的递归深度取决于正在匹配的单词的长度。在最坏的情况下,当成功匹配到单词的最后一个字母时,递归栈的深度会达到 L
  3. 其他变量
    • w = word.toCharArray(): 占用了 O(L) 的空间。
    • DIRECTION 数组:占用 O(1) 的常数空间。
    • 递归函数中的局部变量:占用 O(1) 空间。

综合分析
递归栈的深度是空间复杂度的主要部分。因此,算法的空间复杂度为 O(L),其中 L 是单词 word 的长度。

参考灵神

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/92962.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/92962.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ARM SMMUv3控制器注册过程分析(八)

1.概述 ARM SMMUv3控制器初始化及设备树分析&#xff08;七&#xff09;中描述了IOMMU控制器初始化过程。SMMU驱动最后调用iommu_device_register将其注册到内核中&#xff0c;下面分析一下SMMU控制器注册过程中都做了那些工作。 如下图所示&#xff0c;SMMU控制器注册过程中…

Idefics3:构建和更好地理解视觉-语言模型:洞察与未来方向

温馨提示&#xff1a; 本篇文章已同步至"AI专题精讲" Idefics3&#xff1a;构建和更好地理解视觉-语言模型&#xff1a;洞察与未来方向 摘要 视觉-语言模型&#xff08;VLMs&#xff09;领域&#xff0c;接收图像和文本作为输入并输出文本的模型&#xff0c;正在快…

利用DeepSeek解决kdb+x进行tpch测试的几个问题及使用感受

上文其实没有成功运行tpch的22个标准查询中的任何一个&#xff0c;因为DeepSeek原始给出的导入语句有错&#xff0c;有一些表没有导入。 1.解决类型及长度问题导致的插入tbl文件到内存表失败。 kdbx的Reference card()提到的基本数据类型如下&#xff1a; Basic datatypes n …

SGLang 核心技术详解

SGLang 作为一个高性能的 LLM 服务框架&#xff0c;通过一系列先进的优化技术实现了卓越的推理性能。下面详细解释其核心功能组件&#xff1a; 1. RadixAttention 用于前缀缓存 核心概念 RadixAttention 是 SGLang 独创的前缀缓存机制&#xff0c;基于 Radix Tree&#xff08;基…

精密全波整流电路(四)

精密全波整流电路&#xff08;四&#xff09; 背景说明 [[精密半波整流电路|半波整流]]虽然能实现交直流信号的转换&#xff0c;但是半波整流只能保留信号半个周期的能量&#xff0c;导致信号能量的利用率不高。 因此&#xff0c;在一些场合需要使用到全波整流电路。 同样的&…

深入解读Prometheus 2.33 Series Chunks压缩特性:原理与实践

深入解读Prometheus 2.33 Series Chunks压缩特性&#xff1a;原理与实践 随着监控指标规模不断增长&#xff0c;Prometheus的本地TSDB存储压力日益增大。为提升存储效率&#xff0c;Prometheus 2.33引入了Series Chunks压缩特性&#xff0c;对时间序列数据在写入和存储时进行深…

SpringBoot整合Liquibase提升数据库变更的可控性、安全性、自动化程度(最详细)

为什么要使用liquibase?- 团队协作与版本管理- 当多人&#xff08;或多个小组&#xff09;并行开发、对同一数据库结构进行变更时&#xff0c;如果仅靠手写 SQL 脚本&#xff0c;很 容易产生冲突或漏掉某些变更。- Liquibase 将所有 DDL/DML 操作以“changeset”形式纳入源码管…

数据写入因为汉字引发的异常

spark 数据写hive表,发生 查询分区异常问题 异常: 25107124 19 26.49 ERROR Hive: MelaException(message.Exception thrown when execuling quey. S ELECT DISTINCT ‘org apache.hadop.hive melastore .modelMpartion As"NUCLEUS TYPE,AONCREATE TIME,AO.LAST ACCE…

Springboot项目实现将文件上传到阿里云

Springboot项目实现将文件上传到阿里云 一、概述二、具体步骤 2.1引入阿里云工具 首先先建utils包&#xff0c;然后引入AliOSSUtils类&#xff0c;如下&#xff1a; package com.hechixueyuan.forestfiredetectionsystem.utils;import com.aliyun.oss.OSS; import com.aliyun.o…

如何理解 TCP 是字节流协议?详解

文章目录一、面向字节流二、粘包问题应用层如何解决粘包问题&#xff1f;一、面向字节流 使用 TCP socket 进行网络编程&#xff0c;Linux 内核会给每个 socket 都分配一个发送缓冲区和一个接收缓冲区 由于缓冲区的存在, TCP 读写不需要一一匹配&#xff0c;例如&#xff1a;…

面试问题总结——关于OpenCV(二)

最近小组在面试视觉算法工程师,顺便整理了一波关于OpenCV的面试题目。 有些知识点也不深入,对于写的不对的地方,欢迎指正。 目录 20.像素梯度如何计算? 21.关于开运算和闭运算的理解 22.开运算和闭运算有什么优缺点? 23.图像插值有哪些? 24.图像金字塔的原理 25.边缘检测…

目标导向的强化学习:问题定义与 HER 算法详解—强化学习(19)

目录 1、目标导向的强化学习&#xff1a;问题定义 1.1、 核心要素与符号定义 1.2、 核心问题&#xff1a;稀疏奖励困境 1.3、 学习目标 2、HER&#xff08;Hindsight Experience Replay&#xff09;算法 2.1、 HER 的核心逻辑 2.2、 算法步骤&#xff08;结合 DDPG 举例…

2025 XYD Summer Camp 7.21 智灵班分班考 · Day1

智灵班分班考 Day1 时间线 8:00 在滨兰实验的远古机房中的一个键盘手感爆炸的电脑上开考。开 T1&#xff0c;推了推发现可以 segment tree 优化 dp&#xff0c;由于按空格需要很大的力气导致马蜂被迫改变。后来忍不住了顶着疼痛按空格。8:30 过了样例&#xff0c;但是没有大样…

基于多种主题分析、关键词提取算法的设计与实现【TF-IDF算法、LDA、NMF分解、BERT主题模型】

文章目录有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主一、项目背景二、研究目标与意义三、数据获取与处理四、文本分析与主题建模方法1. 传统方法探索2. 主题模型比较与优化3. 深度语义建模与聚类五、研究成果与应用价值六、总结与展望总结每文一…

MDC(Mapped Diagnostic Context) 的核心介绍与使用教程

关于日志框架中 MDC&#xff08;Mapped Diagnostic Context&#xff09; 的核心介绍与使用教程&#xff0c;结合其在分布式系统中的实际应用场景&#xff0c;分模块说明&#xff1a; 一、MDC 简介 MDC&#xff08;映射诊断上下文&#xff09; 是 SLF4J/Logback 提供的一种线程…

Linux随记(二十一)

一、highgo切换leader&#xff0c;follow - 随记 【待写】二、highgo的etcd未授权访问 - 随记 【待写】三、highgo的etcd未授权访问 - 随记 【待写】3.2、etcd的metric未授权访问 - 随记 【待写】四、安装Elasticsearch 7.17.29 和 Elasticsearch 未授权访问【原理扫描】…

Java环境配置之各类组件下载安装教程整理(jdk、idea、git、maven、mysql、redis)

Java环境配置之各类组件下载安装教程整理&#xff08;jdk、idea、git、maven、mysql、redis&#xff09;1.[安装配置jdk8]2.[安装配置idea]3.[安装配置git]4.[安装配置maven]5.[安装配置postman]6.[安装配置redis和可视化工具]7.[安装配置mysql和可视化工具]8.[安装配置docker]…

配置https ssl证书生成

1.可用openssl生成私钥和自签名证书 安装opensslsudo yum install openssl -y 2.生成ssl证书 365天期限sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \-keyout /etc/ssl/private/nginx-selfsigned.key \-out /etc/ssl/certs/nginx-selfsigned.crt3、按照提示编…

编程语言Java——核心技术篇(四)集合类详解

言不信者行不果&#xff0c;行不敏者言多滞. 目录 4. 集合类 4.1 集合类概述 4.1.1 集合框架遵循原则 4.1.2 集合框架体系 4.2 核心接口和实现类解析 4.2.1 Collection 接口体系 4.2.1.1 Collection 接口核心定义 4.2.1.2 List接口详解 4.2.1.3 Set 接口详解 4.2.1.4…

GaussDB 数据库架构师(八) 等待事件(1)-概述

1、等待事件概述 等待事件&#xff1a;指当数据库会话(session)因资源竞争或依赖无法继续执行时&#xff0c;进入"等待"状态&#xff0c;此时产生的性能事件即等待事件。 2、等待事件本质 性能瓶颈的信号灯&#xff0c;反映CPU,I/O、锁、网络等关键资源的阻塞情况。…