矩陣中的路徑【劍指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