leetcode79.单词搜索 | 回溯算法 | 最通俗易懂的讲解 【java版】

解法一:

1、思路
(回溯) O(n2·3k)

深度优先搜索,我们定义这样一种搜索顺序,即先枚举单词的起点,然后依次枚举单词的每个字母。在这个过程中需要将已经使用过的字母改成一个特殊字母,以避免重复使用字符。

 递归函数设计:

boolean dfs(char[][] board, String word, int u, int x, int y)

 u代表当前枚举到了目标单词word的u个位置。

xy是当前搜索到的二维字符网格的横纵坐标。

搜索过程如下:

1、在二维字符网格中枚举每个单词的起点。
2、从该起点出发向四周搜索单词word,并记录此时枚举到单词word的第u个位置 ( u从0开始)。
3、如果当前搜索的位置(x,y)的元素board[x][y] == word[u],则继续向四周搜索。
4、直到枚举到单词word的最后一个字母返回ture,否则返回false

递归边界:

1、当搜索过程出现当前位置board[x][y] != word[u] ,说明当前路径不合法,返回false。
2、u == word.size() - 1,成功搜索到单词末尾,返回true。
实现细节:

1、从搜索过的位置继续搜索下一层时,需要对当前位置进行标识,表示已经搜索

2、可以使用偏移数组来简化代码。

时间复杂度分析: 单词起点一共有 n2个,单词的每个字母一共有上下左右四个方向可以选择,但由于不能走回头路,所以除了单词首字母外,仅有三种选择。所以总时间复杂度是 O(n2·3k)。
2、参考java代码 :

class Solution {
    public boolean exist(char[][] board, String word) {
        // 对二维字符网格中的每个元素枚举单词起点 i纵j横
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[i].length; j++) {
                if (dfs(board, word, 0, i, j)) {
                    return true;
                }
            }
        }
        // 全部无法匹配自然就是失败
        return false;
    }

    // 存放几个偏移量,方便修改x,y坐标向四周搜索
    int[] dx = new int[] { -1, 0, 1, 0 };
    int[] dy = new int[] { 0, 1, 0, -1 };

    // 题设数组、题设单词、枚举到了word的第几个位置、xy为当然搜索的二维字符网格横纵坐标
    boolean dfs(char[][] board, String word, int u, int x, int y) {
        if (board[x][y] != word.charAt(u))// 递归边界1
        {
            return false;
        } // 如果没有匹配到word第u个字符,直接退出,不符题意

        if (u == word.length() - 1) {// 递归边界2
            return true;// 搜索到单词末尾,完成目标,返回ture
        }

        char char_temp = board[x][y];// 临时存一下
        board[x][y] = '.';// 这里随意,保证前后一致即可,主要是改成特殊字母避免重复搜索

        for (int i = 0; i < 4; i++) {// board[x][y]==word.charAt(u) 为真,且没有到边界,向四周搜索
            int a = x + dx[i]; // 加减一些偏移量实现四周搜索,如i=1时,a、b的值是x-1、y不变,即向左搜索
            int b = y + dy[i];
            if (a < 0 || a >= board.length || b < 0 || b >= board[0].length || board[a][b] == '.') {
                continue; // 当到达搜索边界或者找到了已经搜过的字符,换一个位置重新搜
            }
            if (dfs(board, word, u + 1, a, b)) {
                return true; // 如果没有到上个if检测的检测的边界或者已经搜索过的,则对word的下一个字符进行dfs搜索递归
            }
        }

        // 回溯,将填充的.变回去
        board[x][y] = char_temp;
        return false;
    }
}

解法二:(感觉该回溯解法更易于理解)

public class Solution {
    // 判断给定的单词是否存在于二维字符网格中
    public boolean exist(char[][] board, String word) {
        // 如果网格或者单词为空,则返回false
        if (board == null || board.length == 0 || board[0].length == 0 || word == null || word.length() == 0) {
            return false;
        }
        
        int m = board.length; //网格纵向长度
        int n = board[0].length; //网格横向长度
        boolean[][] visited = new boolean[m][n]; // 记录每个位置是否已经访问过
        
        // 遍历二维字符网格,对每个格子调用backtrack方法进行回溯搜索
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (backtrack(board, visited, word, 0, i, j)) {
                    return true;
                }
            }
        }
        return false; // 遍历完整个网格后仍未找到匹配的单词,返回false
    }
    
    // 回溯搜索匹配单词
    private boolean backtrack(char[][] board, boolean[][] visited, String word, int index, int i, int j) {
        // 如果单词匹配完成,返回true
        if (index == word.length()) {
            return true;
        }
        
        // 判断当前位置是否越界、是否已经访问过、字符是否匹配等情况(特殊情况先排除,再处理)
        if (i < 0 || i >= board.length || j < 0 || j >= board[0].length || visited[i][j] 
            || board[i][j] != word.charAt(index)) {
            return false;
        }
        
        visited[i][j] = true; // 标记当前位置已经访问过
        
        // 依次尝试向上、向下、向左、向右四个方向进行搜索,直到匹配完成或者无法继续搜索为止
        boolean found = backtrack(board, visited, word, index + 1, i + 1, j) || // 向下搜索
                        backtrack(board, visited, word, index + 1, i - 1, j) || // 向上搜索
                        backtrack(board, visited, word, index + 1, i, j + 1) || // 向右搜索
                        backtrack(board, visited, word, index + 1, i, j - 1);   // 向左搜索
        
        visited[i][j] = false; // 回溯,重置访问状态
        
        return found; // 返回搜索结果
    }
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值