Leetcode刷题:剑指offer【面试题12 矩阵中的路径】

文章目录

【面试题12 矩阵中的路径】

难度: 中等

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。

[[“a”,"b",“c”,“e”],
[“s”,"f","c",“s”],
[“a”,“d”,"e",“e”]]

但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。

Leetcode题目对应位置: 面试题12:矩阵中的路径

这道题肯定是用回溯法没错了,但是自己写的(方法1)和大佬写的(方法2)还是有差别的,学习一下啦~

方法 1

回溯法具有递归特性,搜索字符串的过程类似于在每一个位置穷举每一种走法,判断该走法是否符合目标字符串。将路径看作一个栈,当前 n-1 个字符都找到后,若第 n-1 字符对应的格子周围没有第 n 个字符,则退回到第 n-2 个字符,重新定位第 n-1 个字符。

另外题目要求不能重复进入相同的格子,所以需要一个和字符矩阵大小相同的矩阵用于记录路径是否已进入了每个格子。

代码逻辑:

1)创建一个和字符矩阵大小相同的矩阵,初始值全为 False,表示未经过该字符;
2)循环遍历字符矩阵的每个位置,判断以该位置为起点,是否能找到目标字符串路径:

  • 初始时,在矩阵中搜索目标字符串第 1 个字符(idx = 0),判断矩阵当前位置 board[r][c] 上的字符是否与 word[idx] 相同,若相同,则 idx + 1,表示继续搜索下一个字符,且将位置 [r][c] 在 visited 矩阵置 True,表示已被访问过。
  • 递归搜索 [r][c] 上、下、左、右四个方向,若找到了目标字符串路径,则返回 True,否则 idx - 1,回到初始位置,且还原 visited 矩阵。

需要注意边界条件的判断:

1)在递归过程中若找到了当前的目标字符,则 idx +1,当 idx = 目标字符串的长度时,说明已经找完了,直接返回 True;
2)由于每次要找上、下、左、右四个方向,对于矩阵边界的元素来说,下标加 1 减 1 都可能导致下标越界,所以需要判断当前的行、列下标是否在 [0, len-1] 之间,如果越界,相当于该位置未找到目标字符,返回 False。

特殊情况处理:

1)若目标字符串为空,则返回 True,可以看作是任意矩阵都有空字符串的路径
2)若字符矩阵为空,则返回 False

时间复杂度: O(mn3 ^ k),m、n 为矩阵的行和列数,k 为目标字符串长度。最坏情况下,遍历整个字符数组的复杂度为 O(mn),搜索长度为 k 的字符串路径时间为 O(3 ^ k),因为每个矩阵位置排除掉来的方向(不能走回头路),都可以向 3 个方向走。

空间复杂度: O(mn),递归需要 O(k) 的额外空间,最坏情况下 k = mn。visited 矩阵需要 O(mn) 的额外空间。

class Solution:
    def exist(self, board: List[List[str]], word: str) -> bool:
    	if not word: return True
        if not board: return False
        row, col = len(board), len(board[0])
        visited = [[False for c in range(col)] for r in range(row)]

        idx = 0
        for i in range(row):
            for j in range(col):
                if self.hasPathCore(board, word, i, j, idx, visited):
                    return True
        return False

    def hasPathCore(self, board, word, r, c, idx, visited):
        if idx == len(word): return True
        has = False
        # 边界值检验
        if r < 0 or c < 0 or r >= len(board) or c >= len(board[0]):
            return has
        if board[r][c] == word[idx] and not visited[r][c]:
            idx += 1
            visited[r][c] = True
            # 递归查找路径
            has = self.hasPathCore(board, word, r+1, c, idx, visited) or self.hasPathCore(board, word, r-1, c, idx, visited) or self.hasPathCore(board, word, r, c+1, idx, visited) or self.hasPathCore(board, word, r, c-1, idx, visited)
            if not has:
                idx -= 1
                visited[r][c] = False
            return has

在这里插入图片描述

方法 2

和自己的思路差不多,但是这里省去了创建 visited 矩阵,直接用空字符来标记元素是否访问过。注意几个细节:
1)不要将 len(board) 写在 for 循环里,避免每次循环都要计算数组长度
2)board[i][j] = tmp 用于恢复元素值,因为这只代表了在当前 [i, j] 位置的访问情况,到了下一轮循环,所有元素值都要恢复。

为什么这里判断条件是 k == len(word) - 1,而方法一是 == len(word) 呢?因为本方法在前面已经判断了 board[i][j] != word[k] 就返回 False,只是一个先后关系的问题。

时间复杂度:O(mn3^k),
空间复杂度:O(k),最坏情况下 k = mn

class Solution:
    def exist(self, board: List[List[str]], word: str) -> bool:
        def dfs(i, j, k):
        	# 边界值检验
            if not 0 <= i < len(board) or not 0 <= j < len(board[0]) or board[i][j] != word[k]: return False
            if k == len(word) - 1: return True
            tmp, board[i][j] = board[i][j], ''
            res = dfs(i + 1, j, k + 1) or dfs(i - 1, j, k + 1) or dfs(i, j + 1, k + 1) or dfs(i, j - 1, k + 1)
            board[i][j] = tmp
            return res
		
		row = len(board)
		col = len(board[0])
        for i in range(row):
            for j in range(col):
                if dfs(i, j, 0): return True
        return False

代码来源:mian-shi-ti-12-ju-zhen-zhong-de-lu-jing-shen-du-yo
在这里插入图片描述


参考资料:
[1] LeetCode 面试题12:矩阵中的路径
[2] 剑指 offer 第二版
[3] LeetCode 面试题12 参考题解 作者:Krahets

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不吃饭就会放大招

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值