用Java实现单词搜索(LeetCode 79)——回溯算法详解

文章目录

问题描述

给定一个 m x n 的二维字符网格 board 和一个字符串 word,要求判断 word 是否存在于网格中。单词必须通过相邻单元格内的字母按顺序连接形成 ,其中"相邻"单元格指的是水平或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用

示例

输入:

复制代码
board = [
  ['A','B','C','E'],
  ['S','F','C','S'],
  ['A','D','E','E']
]
word = "ABCCED"

输出:true

解释:存在路径 A → B → C → C → E → D


方法思路

核心思想:回溯算法

回溯算法通过递归探索所有可能的路径,并在发现路径不满足条件时及时回溯。具体步骤如下:

  1. 遍历起始点
    以每个单元格为起点,尝试匹配单词的第一个字符。
  2. 递归匹配后续字符
    从当前单元格出发,向四个方向(上、下、左、右)递归搜索下一个字符。
  3. 终止条件
    • 成功条件:已匹配所有字符(递归深度等于单词长度)。
    • 失败条件:越界、字符不匹配、单元格已访问。
  4. 标记已访问单元格
    通过临时修改当前单元格值为 '#' 标记已访问,递归结束后恢复原值。

解决代码

java 复制代码
class Solution {
    public boolean exist(char[][] board, String word) {
        // 遍历所有可能的起点
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[0].length; j++) {
                if (backtrack(board, word, i, j, 0)) {
                    return true;
                }
            }
        }
        return false;
    }
    
    private boolean backtrack(char[][] board, String word, int i, int j, int k) {
        // 成功匹配所有字符
        if (k == word.length()) {
            return true;
        }
        // 越界检查
        if (i < 0 || i >= board.length || j < 0 || j >= board[0].length) {
            return false;
        }
        // 字符不匹配或已访问
        if (board[i][j] != word.charAt(k)) {
            return false;
        }
        
        // 临时标记当前单元格为已访问
        char temp = board[i][j];
        board[i][j] = '#';
        
        // 向四个方向递归搜索
        boolean res = backtrack(board, word, i + 1, j, k + 1)  // 向下
                   || backtrack(board, word, i - 1, j, k + 1)  // 向上
                   || backtrack(board, word, i, j + 1, k + 1)  // 向右
                   || backtrack(board, word, i, j - 1, k + 1);  // 向左
        
        // 恢复单元格原值(回溯)
        board[i][j] = temp;
        return res;
    }
}

代码解释

1. exist 方法

  • 功能:遍历所有可能的起始点,调用回溯函数。
  • 关键点 :若任意一个起点能匹配成功,则直接返回 true

2. backtrack 方法

  • 参数说明
    • i, j:当前单元格坐标。
    • k:当前匹配的字符在单词中的位置。
  • 终止条件
    • k == word.length():已匹配所有字符,返回 true
    • 越界或字符不匹配:返回 false
  • 标记与恢复
    • 标记 :将当前单元格的值改为 '#',表示已访问。
    • 恢复:递归结束后恢复原值,确保其他路径可正常使用该单元格。
  • 递归方向:向四个方向递归搜索下一个字符,利用逻辑或的短路特性优化性能。

关键点解析

1. board[i][j] = '#' 的作用

  • 防止重复访问:临时标记当前单元格已被访问,避免同一路径重复使用。
  • 空间优化 :无需额外使用 visited[][] 数组,空间复杂度从 O(mn) 优化为 O(1)
  • 恢复操作:递归结束后恢复原值,确保其他路径的搜索不受影响。

2. 递归方向的选择

  • 四个方向:上、下、左、右,覆盖所有可能的相邻路径。
  • 逻辑或短路特性 :一旦某条路径返回 true,后续递归不再执行,直接返回结果。

3. 时间复杂度与空间复杂度

  • 时间复杂度O(mn * 4^L),其中 L 为单词长度。最坏情况下需要遍历所有可能的路径。
  • 空间复杂度O(L),递归调用栈的深度由单词长度决定。

总结

  1. 回溯算法的适用性:适合解决路径搜索问题,通过递归探索所有可能性。
  2. 空间优化技巧:通过修改原数组标记访问状态,避免额外空间开销。
  3. 恢复操作的必要性:确保不同路径的独立性,避免状态污染。

通过合理设计终止条件和递归逻辑,该算法能够高效解决单词搜索问题,代码简洁且易于理解。

相关推荐
头发那是一根不剩了10 分钟前
怎么用idea分析hprof文件定位JVM内存问题
java·jvm
技术流浪者14 分钟前
C/C++实践(十)C语言冒泡排序深度解析:发展历史、技术方法与应用场景
c语言·数据结构·c++·算法·排序算法
让代码飞~15 分钟前
maven项目, idea右上角一直显示gradle的同步标识, 如何去掉
java·maven·intellij-idea
张扬飞舞16 分钟前
IntelliJ IDEA克隆项目失败的解决方法
java·ide·intellij-idea
一只码代码的章鱼27 分钟前
spring -MVC-02
java·spring·mvc
ziyue757534 分钟前
idea启用lombok
java·intellij-idea·idea·lombok·软件
I AM_SUN1 小时前
98. 验证二叉搜索树
数据结构·c++·算法·leetcode
tmacfrank1 小时前
Java 原生网络编程(BIO | NIO | Reactor 模式)
java·开发语言·网络
python算法(魔法师版)1 小时前
.NET NativeAOT 指南
java·大数据·linux·jvm·.net
专注VB编程开发20年1 小时前
VB.NET关于接口实现与简化设计的分析,封装其他类
java·前端·数据库