题解 | #矩阵中的路径#

矩阵中的路径

http://www.nowcoder.com/practice/2a49359695a544b8939c77358d29b7e6

题中的主要信息:
  • 矩阵中上下左右随便移动,找到给定字符串的路径
  • 访问可以重复,但是作为路径不能往回走
举一反三:

学习完本题的思路你可以解决如下题目:

JZ13. 机器人的运动范围

方法:递归(推荐使用)

知识点:递归与回溯

递归是一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。因此递归过程,最重要的就是查看能不能讲原本的问题分解为更小的子问题,这是使用递归的关键。

如果是线型递归,子问题直接回到父问题不需要回溯,但是如果是树型递归,父问题有很多分支,我需要从子问题回到父问题,进入另一个子问题。因此回溯是指在递归过程中,从某一分支的子问题回到父问题进入父问题的另一子问题分支,因为有时候进入第一个子问题的时候修改过一些变量,因此回溯的时候会要求改回父问题时的样子才能进入第二子问题分支。

思路:

要在矩阵中找到从某个位置开始,位置不重复的一条路径,路径为某个字符串,那我们肯定是现在矩阵中找到这个位置的起点。没办法直观的找出来,只能遍历矩阵每个位置都当作起点试一试。

找到起点后,它周围的节点是否可以走出剩余字符串子串的路径,该子问题又可以作为一个递归。因此,可以用递归来解决。

  • 终止条件: 到了矩阵的边界或者是下一个字符与这个位置的字符不匹配,或者字符串匹配完成,都需要返回,
  • 返回值: 将每条查询路径是否有这样的字符串返回,即true or false。
  • 本级任务: 检查这个位置的字符与字符串中这个字符是否匹配,并向与它相邻的四个方向(且不是来的方向)延伸子问题。
dfs(matrix, n, m, i - 1, j, word, k + 1, flag)
||dfs(matrix, n, m, i + 1, j, word, k + 1, flag)
||dfs(matrix, n, m, i, j - 1, word, k + 1, flag)
||dfs(matrix, n, m, i , j + 1, word, k + 1, flag)

同时,在走的过程中,要设置一个和矩阵大小相同的bool矩阵表示是否已经访问,如果某个结点访问了,在同一条路线上就不能再访问了,其他路线依旧可以访问,所以若是某条路线不达标,需要回溯,将其改回来。

flag[i][j] = true;
...//递归
//没找到经过此格的,此格未被占用
flag[i][j] = false; 

具体做法:

  • step 1:优先处理矩阵为空的特殊情况。
  • step 2:设置flag数组记录某一次路径中矩阵中的位置是否被经过,因此一条路径不能回头。
  • step 3:遍历矩阵,对每个位置进行递归。
  • step 4:递归查找的时候,到了矩阵的边界或者是下一个字符与这个位置的字符不匹配,或者节点已经访问过了,或者字符串匹配完成都结束递归。
  • step 5:访问节点,修改flag数组,向其他四个方向延伸,回溯的时候修改flag数组。

图示:

图片说明

Java实现代码:

import java.util.*;
public class Solution {
    private boolean dfs(char[][] matrix, int n, int m, int i, int j, String word, int k, boolean[][] flag){
        if(i < 0 || i >= n || j < 0 || j >= m || (matrix[i][j] != word.charAt(k)) || (flag[i][j] == true))
            //下标越界、字符不匹配、已经遍历过不能重复
            return false;
        //k为记录当前第几个字符
        if(k == word.length() - 1) 
            return true;
        flag[i][j] = true;
        //该结点任意方向可行就可
        if(dfs(matrix, n, m, i - 1, j, word, k + 1, flag)
          ||dfs(matrix, n, m, i + 1, j, word, k + 1, flag)
          ||dfs(matrix, n, m, i, j - 1, word, k + 1, flag)
          ||dfs(matrix, n, m, i , j + 1, word, k + 1, flag))
            return true; 
        //没找到经过此格的,此格未被占用
        flag[i][j] = false; 
        return false;
    }
    
    public boolean hasPath (char[][] matrix, String word) {
        //优先处理特殊情况
        if(matrix.length == 0)
            return false;
        int n = matrix.length;
        int m = matrix[0].length;
        //初始化flag矩阵记录是否走过
        boolean[][] flag = new boolean[n][m];
        //遍历矩阵找起点
        for(int i = 0; i < n; i++){  
            for(int j = 0; j < m; j++){
                //通过dfs找到路径
                if(dfs(matrix, n, m, i, j, word, 0, flag))
                    return true;
            }
        }
        return false;
    }
}

C++实现代码:

class Solution {
public:
    bool dfs(vector<vector<char> >& matrix, int n, int m, int i, int j, string word, int k, vector<vector<bool> >& flag){
        if(i < 0 || i >= n || j < 0 || j >= m || (matrix[i][j] != word[k]) || (flag[i][j] == true))
            //下标越界、字符不匹配、已经遍历过不能重复
            return false;
        //k为记录当前第几个字符
        if(k == word.length() - 1) 
            return true;
        flag[i][j] = true;
        //该结点任意方向可行就可
        if(dfs(matrix, n, m, i - 1, j, word, k + 1, flag)
          ||dfs(matrix, n, m, i + 1, j, word, k + 1, flag)
          ||dfs(matrix, n, m, i, j - 1, word, k + 1, flag)
          ||dfs(matrix, n, m, i , j + 1, word, k + 1, flag))
            return true; 
        //没找到经过此格的,此格未被占用
        flag[i][j] = false; 
        return false;
    }
    bool hasPath(vector<vector<char> >& matrix, string word) {
        //优先处理特殊情况
        if(matrix.size() == 0)
            return false;
        int n = matrix.size();
        int m = matrix[0].size();
        //初始化flag矩阵记录是否走过
        vector<vector<bool> > flag(n, vector<bool>(m, false)); 
        //遍历矩阵找起点
        for(int i = 0; i < n; i++){  
            for(int j = 0; j < m; j++){
                //通过dfs找到路径
                if(dfs(matrix, n, m, i, j, word, 0, flag))
                    return true;
            }
        }
        return false;
    }
};

Python实现代码:

class Solution:
    def dfs(self, matrix: List[List[str]], n: int, m: int, i: int, j: int, word: str, k: int, flag: List[List[bool]]) -> bool:
        if i < 0 or i >= n or j < 0 or j >= m or (matrix[i][j] != word[k]) or flag[i][j]:
            #下标越界、字符不匹配、已经遍历过不能重复
            return False
        #k为记录当前第几个字符
        if k == len(word) - 1:
            return True
        flag[i][j] = True
        #该结点任意方向可行就可
        if (self.dfs(matrix, n, m, i - 1, j, word, k + 1, flag) 
            or self.dfs(matrix, n, m, i + 1, j, word, k + 1, flag) 
            or self.dfs(matrix, n, m, i, j - 1, word, k + 1, flag) 
            or self.dfs(matrix, n, m, i , j + 1, word, k + 1, flag)):
            return True 
        #没找到经过此格的,此格未被占用
        flag[i][j] = False 
        return False

    def hasPath(self , matrix: List[List[str]], word: str) -> bool:
        if len(matrix) == 0:
            return False
        n = len(matrix)
        m = len(matrix[0])
        #初始化flag矩阵记录是否走过
        flag = [[False for i in range (m)] for j in range(n)]
        #遍历矩阵找起点
        for i in range(n):
            for j in range(m):
                #通过dfs找到路径
                if self.dfs(matrix, n, m, i, j, word, 0, flag):
                    return True
        return False

复杂度分析:

  • 时间复杂度:O(mn3k)O(mn*3^k),其中mmnn为矩阵的边长,kk为字符串的长度,遍历一次矩阵,每次条递归最坏遍历深度为kk,看起来是四个方向,但是有一个方向是来的方向不重复访问,所以是三叉树型递归,因此递归复杂度为O(k3)O(k^3)
  • 空间复杂度:O(mn)O(mn),辅助二维数组记录是否走过某节点使用了空间
全部评论
想问一下,为什么递归时间复杂度是O(k^3) 递归树深度是k,每个节点有三个子节点,最坏的情况下,总节点数不应该是 首项为1,公比为3,总项数为k的等比数列 求和 Sn=首项(1-公比的k次方)/1-公比(公比≠1),结果是O(3^k)吗,最终整个的时间复杂度是O(mn * 3^k)。
点赞 回复 分享
发布于 2022-09-03 11:03 广东
这题真的复杂,虽然不难。
点赞 回复 分享
发布于 2022-09-23 11:07 重庆
题目有歧义,不看答案根本无法准确理解。可能被错误理解为如下意思, 路径包含字符串中的所有字符即可,不要求连续,不要求顺序正确,比如路径“adbdc”包含了字符串“acb”的所有字符; 路径按顺序包含字符串中的字符,不要求连续,比如路径“adbdc”包含了字符串“abc”的所有字符;
点赞 回复 分享
发布于 2023-07-28 11:05 浙江

相关推荐

11-05 07:29
贵州大学 Java
点赞 评论 收藏
分享
Hello_WordN:咱就是说,除了生命其他都是小事,希望面试官平安,希望各位平时也多注意安全
点赞 评论 收藏
分享
17 11 评论
分享
牛客网
牛客企业服务