【LeetCode】面试题12. 矩阵中的路径
题目:
思路:
因为可以从矩阵中任意一格开始,所以拿到题之后的思路为: 遍历整个矩阵,尝试把每个点当作起始点去搜索路径,直到找到或者遍历完。考虑到有点暴力,感觉不是最优方法,所以一直在思考更优的方法(也被它动态规划的标签误导了),陷入了瓶颈,没有把之前的暴力思路抽象到深度优先搜索的方法论。
另外一直在把思路复杂化,考虑记录每次进入某个节点的方向,避免形成循环。而参考了代码中的思路之后,将当前值置为"/",避免了循环。从而能够抽象到特别简单的DFS中,只需要考虑边界就可以了,避免了复杂的条件判断。
也想到了另一个方法: 对整个矩阵遍历K轮,第k轮遍历找到矩阵中等于目标字符串位置k的字符,查看该位置的上下左右,是否已经组成了前k-1个目标字符串,需要注意的点有: 避免循环,到达某个点的路径可能有多条,都需要记录下来。
代码:
Python
# Solution1 84/87超时
# [["A","B","C","E"],["S","F","E","S"],["A","D","E","E"]] "ABCESEEEFS"
# [["a"]] "a"
# [["a", "a"], ["a", "a"]] "aaaaa"
import copy
class Solution(object):
def exist(self, board, word):
"""
:type board: List[List[str]]
:type word: str
:rtype: bool
"""
x = len(board)
y = len(board[0])
# 构造now记录每个位置的当前路径[[[set(), set()], []], [[], []]]
# 每个点通过一个list记录,有几条路径list中就有几个元素,每个元素是set,保证路径不重复
# 构造过程也是第一个目标字符的遍历过程
now = list()
for i in range(x):
tmp = list()
for j in range(y):
if board[i][j] == word[0]: # 目标字符串长度为1的情况
if len(word) == 1:
return True
# 保证是list(set(tuple))结构
a = set()
a.add((i, j))
tt = list()
tt.append(a)
tmp.append(tt)
else:
tmp.append(list())
now.append(tmp)
# 遍历剩余目标字符
for c in range(1, len(word)):
# 因为会修改now,避免修改后的now被用于当前轮次
pre = copy.deepcopy(now)
this_char = word[c]
for i in range(x):
for j in range(y):
now[i][j] = list()
# 遍历整个矩阵,对等于当前目标字符的位置。查看它的上下左右,是否存在长度为c的前缀路径,把它们全部找出来
if board[i][j] == this_char:
pre_list = list()
if i - 1 >= 0 and len(pre[i-1][j]) > 0:
pre_list.extend(pre[i-1][j])
if j - 1 >= 0 and len(pre[i][j-1]) > 0:
pre_list.extend(pre[i][j-1])
if i + 1 < x and len(pre[i+1][j]) > 0:
pre_list.extend(pre[i+1][j])
if j + 1 < y and len(pre[i][j+1]) > 0:
pre_list.extend(pre[i][j+1])
# 对每一个前缀路径,添加当前节点
# 如果重复则整个前缀路径失效,判断是否找到
for e in pre_list:
ee = copy.deepcopy(e) # 这里不copy会修改pre的数据??
ee.add((i, j))
if len(ee) == len(word):
return True
if len(ee) == (c+1):
now[i][j].append(ee)
return False
# Solution2
class Solution(object):
def exist(self, board, word):
"""
:type board: List[List[str]]
:type word: str
:rtype: 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
# 把每个节点作为起始点开始遍历
for i in range(len(board)):
for j in range(len(board[0])):
if dfs(i, j, 0): return True
return False