大家好,我是程序员吴师兄,欢迎来到 图解剑指 Offer 结构化专栏,在这个专栏里我将和大家一起学习如何用结构化的思维来思考、解题、写代码,希望能帮助你即使在面试的时候紧张也能做对。
今天分享的题目来源于的剑指 Offer 系列 面试题 12. 矩阵中的路径。
题目汇总链接:https://www.algomooc.com/hi-offer
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。
如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。
[["a","b","c","e"], ["s","f","c","s"], ["a","d","e","e"]]
但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。
示例 1:
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true
示例 2:
输入:board = [["a","b"],["c","d"]], word = "abcd"
输出:false
提示:
1 <= board.length <= 200
1 <= board[i].length <= 200
我们依旧用 四步分析法 进行结构化的分析。
首先看一下矩阵的初始状态。
我们需要在这个矩阵中寻找目标字符串 bfce,第一步要做的就是先匹配上目标字符串的第一个元素 b ,我们从矩阵的第一行第一列的元素开始匹配,找到了 a 。
目标字符串为 bfce,此时查找第一个元素为 a ,与目标字符串的第一个元素 b 不相同,需要在四个方向搜索,看看能不能找到符合要求的元素,我们按照上左下右的顺序进行遍历寻找。
为了保证不重复访问节点,可以将这条路径上已经访问过的节点,修改为不在 word 当中的一个字符,保证以后再次访问时不会重复访问,这里我们将其修改为特殊字符 # 。
修改完后会出现一种情况,当前的节点元素与目标元素相匹配,但是在它的四个方向的节点中都找不到可以匹配到目标下一元素的节点。
比如此时当前元素 c 与目标元素 c 相匹配,但是目标下一元素为 x,而在当前元素的四个方向上都找不到 x ,需要把这个点回退,根据之前的操作,当前的节点被修改为了 #,所以为了能够回退成功,再回退操作时需要重新将 # 修改回原来的元素。
本题提供了一个矩阵,矩阵是一个二维数组,需要我们在二维数组中进行搜索,为了能够覆盖所有的情况,必然要使用两个嵌套的循环。
在搜索过程中,当遇到匹配成功的元素,搜索其下一元素的操作与当前的操作一致,即可以使用递归。
board
中的行列索引 i
和 j
,当前目标字符在word
中的索引 k
。k = len(word) - 1
,即字符串 word
已全部匹配。board[ i ] [ j ]
修改为特殊字符 # ,代表此元素已访问过,防止之后搜索时重复访问。board[ i ] [ j ]
元素还原至初始值,即 word[k] 。res
,代表是否搜索到目标字符串。剑指 Offer 12. 矩阵中的路径.002
剑指 Offer 12. 矩阵中的路径.003
剑指 Offer 12. 矩阵中的路径.004
剑指 Offer 12. 矩阵中的路径.005
剑指 Offer 12. 矩阵中的路径.006
剑指 Offer 12. 矩阵中的路径.007
剑指 Offer 12. 矩阵中的路径.008
剑指 Offer 12. 矩阵中的路径.009
剑指 Offer 12. 矩阵中的路径.010
剑指 Offer 12. 矩阵中的路径.011
剑指 Offer 12. 矩阵中的路径.012
剑指 Offer 12. 矩阵中的路径.013
剑指 Offer 12. 矩阵中的路径.014
剑指 Offer 12. 矩阵中的路径.015
剑指 Offer 12. 矩阵中的路径.016
剑指 Offer 12. 矩阵中的路径.017
剑指 Offer 12. 矩阵中的路径.018
剑指 Offer 12. 矩阵中的路径.019
剑指 Offer 12. 矩阵中的路径.020
剑指 Offer 12. 矩阵中的路径.021
剑指 Offer 12. 矩阵中的路径.022
剑指 Offer 12. 矩阵中的路径.023
剑指 Offer 12. 矩阵中的路径.024
剑指 Offer 12. 矩阵中的路径.025
剑指 Offer 12. 矩阵中的路径.026
剑指 Offer 12. 矩阵中的路径.027
剑指 Offer 12. 矩阵中的路径.028
剑指 Offer 12. 矩阵中的路径.029
剑指 Offer 12. 矩阵中的路径.030
剑指 Offer 12. 矩阵中的路径.031
剑指 Offer 12. 矩阵中的路径.032
// 登录 AlgoMooc 官网获取更多算法图解
// https://www.algomooc.com
class Solution {
public boolean exist(char[][] board, String word) {
// 先将字符串进行拆分,一个一个元素进行匹配
char[] words = word.toCharArray();
// 通过两层嵌套,覆盖所有的情况
for(int i = 0; i < board.length; i++) {
for(int j = 0; j < board[0].length; j++) {
// 以该元素为起始点,递归检查是否符合要求
if(dfs(board, words, i, j, 0)) return true;
}
}
return false;
}
boolean dfs(char[][] board, char[] word, int i, int j, int k) {
// 边界情况判断
// 行越界
// 列越界
// 矩阵元素已访问过
if(i >= board.length || i < 0 || j >= board[0].length || j < 0 || board[i][j] != word[k]) return false;
// 之前已经和目标字符串匹配成功了 length - 1 个字符,此时又匹配成功了最后一个元素,直接返回结果
if(k == word.length - 1) return true;
// 标记当前矩阵元素,将其修改为特殊字符 # ,代表此元素已访问过,防止之后搜索时重复访问。
board[i][j] = '#';
// 搜索元素的四个方向 上 左 下 右,匹配下一个目标元素
boolean res = dfs( board , word , i , j - 1 , k + 1 )
|| dfs( board , word , i - 1 , j , k + 1 )
|| dfs( board , word , i , j + 1 , k + 1 )
|| dfs( board , word , i + 1 , j , k + 1 );
// 回退时还原当前矩阵元素
board[i][j] = word[k];
// 返回结果
return res;
}
}
注意 :搜索元素的四个方向 上 左 下 右,匹配下一个目标元素时的代码不要拆分,否则会出现超出时间限制,因为这样做相当于每一个方向都进行了搜索,而事实上不需要,只需要找到一条符合要求的路径即可。
boolean up = dfs(board, word, i, j - 1, k + 1);
boolean left = dfs(board, word, i - 1, j , k + 1);
boolean down = dfs(board, word, i, j + 1, k + 1);
boolean right = dfs(board, word, i + 1, j , k + 1);
boolean res = up || left || down || right;
时间复杂为 O( 4k M N ),其中 M,N 分别为矩阵行列长度, K 为字符串 word
的长度,每个字符都有 4 个方向。
空间复杂度为 O(K) 。
1、https://leetcode-cn.com/problems/ju-zhen-zhong-de-lu-jing-lcof/solution/mian-shi-ti-12-ju-zhen-zhong-de-lu-jing-shen-du-yo/
2、https://leetcode-cn.com/problems/ju-zhen-zhong-de-lu-jing-lcof/
3、https://www.algomooc.com