【递归算法】单词搜索

文章摘要:

  • 题目要求在给定的二维字符矩阵中查找是否存在指定的字符串,只能通过相邻方格(上下左右)且不能重复使用同一方格。采用深度优先搜索(DFS)策略,通过回溯法遍历所有可能的路径。 算法核心步骤: 遍历矩阵,找到与目标字符串首字符匹配的位置 使用DFS从该位置开始搜索后续字符 维护访问标记数组避免重复使用 通过方向数组处理四个相邻位置 找到完整字符串返回true,否则回溯并继续搜索 时间复杂度:O(mn*3^L),其中mn为矩阵大小,L为字符串长度。空间复杂度:O(mn)用于存储访问标记。

文章目录

79. 单词搜索

一、题目解析

题目给出一个 m x n 的二维字符矩阵 board,并且给出一个字符串 word,我们需要从这个二位字符矩阵中找是否存在 字符串 word,若存在,返回 true,否则返回 false

这里需要注意,只能是相邻 的方格,不可以斜着找,并且每一个方格只能找一次而不能重复使用

例如,题目给出:

  • board = [ [ 'A', 'B', 'C', 'E' ], [ 'S', 'F', 'C', 'S' ], [ 'A', 'D', 'E', 'E' ] ], word = "ABCCED",

board 是如下所示的一个矩阵:

A B C E
S F C S
A D E E

我们需要从这个矩阵中找到字符串 ABCCED,如下所示:

A B C E
S F C S
A D E E

因为可以在矩阵中找到对应的字符串,所以返回 true

若 word = "SEE",也可以找到:

A B C E
S F C S
A D E E

此时也是返回 true

若 word = "ABCB",则无法找到,返回 false

A B C E
S F C S
A D E E

二、算法原理 + 代码实现

我们这里采用暴搜的策略,每一个可能的路径都搜索出来,若遇到存在的情况就返回 true,否则就返回 false。

在二维矩阵中搜索且不能重复,常常需要创建一个布尔类型的 visit 二维数组,其大小设置和 board 一样,目的是为了记录二维矩阵中的每一个方格的使用状态:若未被使用则默认是 false;若已被使用则改成 true。

决策树

基于例子:board = [ [ 'A', 'B', 'C', 'E' ], [ 'S', 'F', 'C', 'S' ], [ 'A', 'D', 'E', 'E' ] ], word = "ABCCED" 画出部分决策树如下:

大致过程如下:

  1. 我们首先遍历 board,找到 word第一个字符 "A" ,然后基于这个位置开始递归(使用了这个位置不要忘记将该位置在 visit 中的值更新为 true),递归的时候需要知道当前位置在 board 中的坐标
  2. 当前在第一个位置 "A" 处,我们在该位置的上下左右四个位置 找 word 的第二个字符 "B",很显然就是位置 "A" 的右边,接下来更新位置的状态后基于这个位置继续递归,以此类推,直到我们找到 word 的最后一个字符,就返回 true。

这里我们需要注意的步骤是如何在某个位置寻找上下左右呢?

一个常用的做法是定义两个向量坐标,具体原理是:

坐标 [ i, j ] 的上下左右四个坐标都是在 i 和 j 上加上了 0、1、-1 的:

  • 上下坐标是 [ i + (-1), j + 0] 和 [ i + 1, j + 0 ];
  • 左右坐标是 [ i + 0, j + (-1)] 和 [ i + 0, j + 1 ]。

我们定义两个向量坐标:dx = {0, 0, -1, 1},dy = {-1, 1, 0, 0}

这样,我们循环四次就可以依次访问一个位置 [ row, col ] 的上下左右坐标,并使用 x 和 y 代替他们的坐标,其中:x = row + dx[k],y = col + dy[k](k 表示循环的次数),而 board[x][y] 也就表示这个坐标在矩阵中的位置(需要注意 x 和 y 不能越界)。

全局变量

我们可以将题目给出的 boardword 都改成全局变量,这样递归的时候可以不用作为参数了,方便很多(我个人认为)。这里也可以将 word 设置为字符数组,方便后续的操作。

然后需要一个大小与 board 一样的布尔数组 visit,为了遍历方便,将 board 的大小 mn 也改成全局变量。

在访问某个位置的上下左右的时候需要用到两个方向数组 dxdy

java 复制代码
char[][] board;
char[] word;
boolean[][] visit;
int m, n;
// 方向数组
int[] dx = {0, 0, -1, 1};
int[] dy = {-1, 1, 0, 0};

dfs 函数

函数头

按照我们的思路,在递归的时候需要直到某个位置在 board 矩阵中的坐标,所以需要有两个参数 rowcol

同时,为了明确每一次递归所要查找字符是在 word 中的哪一个字符,需要一个下标 pos

这里我们需要做一个和 解数独 一样的操作:把 dfs 函数的返回值设置为布尔类型,这是因为我们需要知道本次的选择是否正确,如果正确就返回 true,不正确就返回 false。

java 复制代码
boolean dfs(int row, int col, int pos)

函数体

由于 dfs 函数的参数涉及到下标 pos,我们首先要保证不越界。

然后我们循环四次,依次访问当前位置 [ row, col ] 的上下左右四个位置并判断在 board 中对应位置的字符是否是本次递归想要寻找的字符,找到之后线更新位置状态再继续递归,否则还原现场。

如果循环四次结束后仍未返回,则表示四个位置的字符都不符合,返回 false。

细节问题

回溯

本题有两个地方需要回溯:遍历 board 的时候需要回溯;dfs 函数中访问某位置的上下左右的时候也需要回溯。

具体的回溯操作就是将 visit 数组更改回 false。

剪枝

我们的解题思想是暴搜,因此不涉及到剪枝操作。

递归出口

在 dfs 函数的参数涉及到了下标 pos,因此需要保证 pos 不能越界:下标 pos 等于 word 的长度时刚好是递归出口。

代码实现

java 复制代码
class Solution {
    char[][] board;
    char[] word;
    boolean[][] visit;
    int m, n;
    // 方向数组
    int[] dx = {0, 0, -1, 1};
    int[] dy = {-1, 1, 0, 0};

    public boolean exist(char[][] givenBoard, String givenWord) {
        board = givenBoard; word = givenWord.toCharArray();
        m = board.length; n = board[0].length;
        visit = new boolean[m][n];

		// 遍历board
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                // 在board中找到word的第一个字符
                if (board[i][j] == word[0]) {
                    visit[i][j] = true;
                    if (dfs(i, j, 1)) return true;
                    visit[i][j] = false;
                }
            }
        }
        return false;
    }

    public boolean dfs(int row, int col, int pos) {
        // 递归出口,保证pos不越界
        if (pos == word.length) {
            return true;
        }

        // 从[row,col]这个位置向四个方向寻找下一个位置
        for (int k = 0; k < 4; k++) {
            int x = row +dx[k], y = col + dy[k];
            if (x >= 0 && x < m && y >= 0 && y < n) {
                if (!visit[x][y] && board[x][y] == word[pos]) {
                    // 找到对应的字符
                    visit[x][y] = true;
                    if (dfs(x, y, pos + 1)) return true;
                    visit[x][y] = false;
                }
            }
        }
        // 到此处若还未返回,说明四个方向都没有匹配到相应字符,返回false
        return false;
    }
}

文章到此处就告一段落啦,若有错误请尽管指出~

相关推荐
幸运的大号暖贴2 小时前
解决Vibe Coding时Idea经常不自动git add问题
java·人工智能·git·intellij-idea·claudecode·opencode
m0_716255002 小时前
第一部分 数据开发 面试全题 模拟口述版(自问自答)
java·数据库·面试
咚咚王者2 小时前
人工智能之RAG工程 第一章 RAG 基础与前置知识
人工智能·算法
handler012 小时前
【算法模板】最小生成树:稠密图选 Prim,稀疏图选 Kruskal
c语言·数据结构·c++·算法
SuperherRo2 小时前
服务攻防-Java组件安全&FastJson&高版本JNDI&不出网C3P0&编码绕WAF&写入文件CI链
java·安全·fastjson·waf·不出网·高版本·写入文件
丑八怪大丑2 小时前
SQL数据类型
java·数据库·sql
Nyarlathotep01132 小时前
并发集合类(3):LinkedBlockingQueue
java·后端
李温候2 小时前
互联网大厂Java求职者面试全攻略
java·数据库·面试·orm·构建工具·web框架·互联网大厂
Chase_______3 小时前
LeetCode 2461 & 1423:定长滑窗变体精讲,从 HashMap 判重到正难则反的转化技巧
算法·leetcode·职场和发展