矩阵中的路径【剑指offer】- 回溯、BFS
- 题目:
- 输入:path = “ABCCED” ,matrix = “ABCESFCSADEE”,rows = 3,cols = 4
- 输出: True or False
1. 回溯法(递归):
- 思路:
- 首先矩阵中每一个字母都有可能是path的开头,因此遍历所有矩阵元素,只要有一次递归返回True,则返回True;
- 递归思路:
- 每一步递归做什么:给定当前matrix、当前位置r,c、当前path、和path中的第i个元素,判断从矩阵从(r,c)开始,path从第i个元素开始,两者是否匹配;
- 终止条件:
- 如果
i==len(path)
,返回True,说明指针i指到了len(path)
位置,说明path前面元素都匹配完毕;
- 如果(r,c)越界了或者(r,c)位置已经被访问过了,返回False;
- 如果(r,c)位置元素和
path[i]
不匹配,返回False;
- 返回什么:
- 如果(r,c)和
path[i]
匹配成功,先更改matrix使得当前位置置为‘#’(已遍历)(该更改后的matrix会被直接传到下一轮递归),再判断下一位置是否匹配。下一位置有四种:上、下、左、右,使用or连接,只要有一个返回True就可以了
该方法和官方方法不同之处:不需要新建一个visted矩阵来记录遍历情况,直接在matrix上进行更改,并将改变好的matrix传入下一次递归。但是需要注意的地方在于第10行,因为矩阵的每个字母都有可能作为开头,因此遍历所有字母时,传入的matrix应该是同样干净的(没有被遍历过),因此使用了一个深拷贝(python3也可以使用copy方法)。
class Solution:
def hasPath(self, matrix, rows, cols, path):
if not matrix or not path:
return False
matrix = list(matrix)
hasornot = False
for r in range(rows):
for c in range(cols):
if self.haspath(matrix[:], rows, cols, r, c, path, 0):
return True
return False
def haspath(self, matrix, rows, cols, r, c, path, i):
if i==len(path):
return True
if not 0<=r<rows or not 0<=c<cols or matrix[r*cols + c] == '#':
return False
if matrix[r*cols + c] != path[i]:
return False
else:
matrix[r*cols + c] = '#'
return self.haspath(matrix, rows, cols, r, c+1, path, i+1) or \
self.haspath(matrix, rows, cols, r, c-1, path, i+1) or \
self.haspath(matrix, rows, cols, r+1, c, path, i+1) or \
self.haspath(matrix, rows, cols, r-1, c, path, i+1)
2. BFS广度优先遍历:
思考:
- 递归的时候,需要不断向子递归函数传入当前已经遍历过的矩阵,矩阵检测位置(r,c+1)/(r,c-1)/(r+1,c)(r-1,c),还有path的检测位置(i+1)。这三者的存储也可以通过BFS来实现,设想一颗四叉树,每一个节点存储(当前遍历过的矩阵,矩阵检测位置,path检测位置),然后只要path的检测位置i移动到了最后,就返回True,否则继续建立新的子节点(四个方向)
这里需要注意的地方:第10行处的matrix和path必须是深拷贝,原因同上。(这里之所以path也深拷贝是因为没有使用指针i来遍历path,而是直接遍历一个pop一个,因此path是动态的,所以在初始化时要深拷贝)
还有在存储四个子节点时,cur_matrix和cur_path同样需要深度拷贝,因为两者都是动态的,有人可能会问为什么回溯法中调用子递归函数时不需要深度拷贝?那是因为递归函数在参数传入的过程就已经深度拷贝了。
因此无论哪种方法,必然都要遍历所有可能的路径,空间消耗也是一样的。
class Solution:
def hasPath(self, matrix, rows, cols, path):
if not path:
return False
matrix = list(matrix)
path = list(path)
for i in range(rows):
for j in range(cols):
if self.BFS_search(matrix[:], i, j, rows, cols, path[:]):
return True
return False
def BFS_search(self, matrix, i, j, rows, cols, path):
queue = []
queue.append(((i,j), matrix, path))
while queue:
cur_pos, cur_matrix, cur_path = queue.pop(0)
if not cur_path:
return True
r = cur_pos[0]
c = cur_pos[1]
if 0<=r<rows and 0<=c<cols and cur_matrix[r*cols + c] != '#' and matrix[r*cols + c] == cur_path[0]:
cur_matrix[r*cols + c] = '#'
cur_path.pop(0)
queue.append(((r+1,c), cur_matrix[:], cur_path[:]))
queue.append(((r-1,c), cur_matrix[:], cur_path[:]))
queue.append(((r,c+1), cur_matrix[:], cur_path[:]))
queue.append(((r,c-1), cur_matrix[:], cur_path[:]))
return False