[LeetCode] #1091. Shortest Path in Binary Matrix
題目
Given an n x n
binary matrix grid
, return the length of the shortest clear path in the matrix. If there is no clear path, return -1
.
A clear path in a binary matrix is a path from the top-left cell (i.e., (0, 0)
) to the bottom-right cell (i.e., (n - 1, n - 1)
) such that:
- All the visited cells of the path are
0
. - All the adjacent cells of the path are 8-directionally connected (i.e., they are different and they share an edge or a corner).
The length of a clear path is the number of visited cells of this path.
直覺解
構想:
用遞迴。從 grid[0][0] 的點出發,按照「右下、下、右、左下、右上」的優先順序,依序嘗試下一個點要往哪個方向走,直到走到 grid[n-1][n-1] 或沒路了為止。
python 3 實作(失敗):
def shortestPathBinaryMatrix(self, grid: List[List[int]]) -> int:
ans = -1
n = len(grid)
if grid[n-1][n-1] != 0:
return ans
def findPath(current, ans):
i, j = current
if i >= n or j >=n:
return -1
if grid[i][j] != 0:
return -1
ans += 1
if i == n-1 and j == n-1:
return ans
result = findPath((i+1, j+1), ans)
if result > 0:
return result
result = findPath((i+1, j), ans)
if result > 0:
return result
result = findPath((i, j+1), ans)
if result > 0:
return result
result = findPath((i+1, j-1), ans)
if result > 0:
return result
result = findPath((i-1, j+1), ans)
if result > 0:
return result
return -1
current = (0, 0)
return findPath(current, 0)
執行結果:
Runtime Error Message: RecursionError: maximum recursion depth exceeded in comparison
過程中犯過的錯誤
- 忘記設定,如果到達 grid[n-1][n-1] 的話就可以結束。
構想:
改良的地方:放棄遞迴,用 stack。
自己的 python 3 實作(失敗):
def shortestPathBinaryMatrix(self, grid: List[List[int]]) -> int:
n = len(grid)
if grid[n-1][n-1] != 0:
return -1
stack = [(0, 0, 0)]
while stack:
i, j, count = stack.pop()
if i >= n or j >= n:
continue
if i < 0 or j < 0:
continue
if grid[i][j] != 0:
continue
count += 1
if i == n-1 and j == n-1:
return count
stack.extend([(i-1, j+1, count), (i+1, j-1, count), (i, j+1, count), (i+1, j, count) ,(i+1, j+1, count)])
return -1
執行結果:
Time Limit Exceeded
Last executed input:
[[0,1,1,0,0,0],[0,1,0,1,1,0],[0,1,1,0,1,0],[0,0,0,1,1,0],[1,1,1,1,1,0],[1,1,1,1,1,0]]
錯誤發生原因:
- 因為沒有限制「不能走回頭路」,結果在 grid[3][2] 和 grid[2][3] 之間徘徊,造成無窮迴圈。
- 只考慮了五個方向,但其實下一步能走的方向總共有八個。
構想:
改良的地方:按照「右下、下、右、左下、右上、左、上、右上」的優先順序,依序嘗試。走過的點,就把值從 0 改成 2,以免走回頭路。自己的 python 3 實作 (失敗):
def shortestPathBinaryMatrix(self, grid: List[List[int]]) -> int:
n = len(grid)
if grid[n-1][n-1] != 0:
return -1
stack = [(0, 0, 0)]
while stack:
i, j, count = stack.pop()
if i >= n or j >= n:
continue
if i < 0 or j < 0:
continue
if grid[i][j] != 0:
continue
grid[i][j] = 2
count += 1
if i == n-1 and j == n-1:
return count
stack.extend([(i-1, j-1, count), (i-1, j, count), (i, j-1, count), (i-1, j+1, count), (i+1, j-1, count), (i, j+1, count), (i+1, j, count) ,(i+1, j+1, count)])
return -1
執行結果:
Wrong Answer
Input:
[[0,1,0,0,0,0],[0,1,0,1,1,0],[0,1,1,0,1,0],[0,0,0,0,1,0],[1,1,1,1,1,0],[1,1,1,1,1,0]]
Output: 15
Expected: 14
錯誤發生原因:
- 在 grid[3][2] 這個點時,最短路徑應該是往右上走,但依照我的優先順序,會變成先往右再往上,於是就多了一步。
構想:
改良的地方:優先順序改成「右下、左下、右上、下、右、左上、左、上」
自己的 python 3 實作 (失敗):
def shortestPathBinaryMatrix(self, grid: List[List[int]]) -> int:
n = len(grid)
if grid[n-1][n-1] != 0:
return -1
stack = [(0, 0, 0)]
while stack:
i, j, count = stack.pop()
if i >= n or j >= n:
continue
if i > 0 or j > 0:
continue
if grid[i][j] != 0:
continue
grid[i][j] = 2
count += 1
if i == n-1 and j == n-1:
return count
stack.extend([(i-1, j, count), (i, j-1, count), (i-1, j-1, count), (i, j+1, count), (i+1, j, count), (i-1, j+1, count), (i+1, j-1, count), (i+1, j+1, count)])
return -1
執行結果:
Wrong Answer
Input:
[[0,0,0],[0,1,0],[0,0,0]]
Output: 5
Expected: 4
錯誤發生原因:
- 在 grid[2][1] 這個點時,最短路徑應該是往右走,但依照我的優先順序,會變成先往右上再往下,於是就多了一步。
- 感覺起來,除了設定優先順序以外,還要設定別的機制,才能每次都找到最短路徑。
研究別人的解
來源:Python BFS
別人的 python 3 實作:
def shortestPathBinaryMatrix(self, grid: List[List[int]]) -> int:
N = len(grid)
dis = [[-1] * N for _ in range(N)]
dis[0][0] = 1
q = [(0, 0)] if grid[0][0] == 0 else []
while q:
r, c = q.pop(0)
for i, j in [(r+1, c+1), (r+1, c), (r, c+1), (r-1, c+1), (r+1, c-1), (r-1, c), (r, c-1), (r-1, c-1)]:
if 0 <= i < N and 0 <= j < N and dis[i][j] == -1 and grid[i][j] == 0:
dis[i][j] = dis[r][c] + 1
q.append((i, j))
if i == j == N-1:
break
return dis[-1][-1]
效能:
Runtime: 612 ms, faster than 84.87% of Python3 online submissions for Shortest Path in Binary Matrix.
Memory Usage: 14.7 MB, less than 63.06% of Python3 online submissions for Shortest Path in Binary Matrix.
研究別人的實作時,加上的 print
:
def shortestPathBinaryMatrix(self, grid: List[List[int]]) -> int:
N = len(grid)
dis = [[-1] * N for _ in range(N)]
dis[0][0] = 1
q = [(0, 0)] if grid[0][0] == 0 else []
while q:
print('-------')
print(q)
r, c = q.pop(0)
for i, j in [(r+1, c+1), (r+1, c), (r, c+1), (r-1, c+1), (r+1, c-1), (r-1, c), (r, c-1), (r-1, c-1)]:
print(i, j)
if 0 <= i < N and 0 <= j < N and dis[i][j] == -1 and grid[i][j] == 0:
print('filtered')
dis[i][j] = dis[r][c] + 1
q.append((i, j))
if i == j == N-1:
break
return dis[-1][-1]
研究感想:
我的寫法,會在 stack 裡面塞一堆「下一步要往哪個方向」,而且找到方向之後,不會把沒有被選到的方向清空,所以 stack 裡面有很多無用的資訊。但是這個人的寫法就沒有這個問題。
避免走回頭路,我的寫法,是把走過的 cell 值改成 2。他則是做了一個每個 cell 都是 -1 的新 grid,走過的 cell 就改成「這條路的第幾步」(例如,dis[0][0] 固定都是第一步,而最後的回傳值則是 dis[-1][-1]),所以值是 -1 的話代表沒有走過。
他的優先順序是設定成「右下、下、右、右上、左下、上、左、左上」。我如果用這個順序的話,在 [[0,1,0,0,0,0],[0,1,0,1,1,0],[0,1,1,0,1,0],[0,0,0,0,1,0],[1,1,1,1,1,0],[1,1,1,1,1,0]]
這一題會多走一步。以此題目為例,站在 grid[3][2]
時,我的寫法,找到右邊是可以走的時候,就會直接往那裡走了;但是他的寫法,找到右邊是可以走的時候,他還會繼續看右上能不能走、左下能不能走、⋯⋯直到迭代完所有的可能性,等到下一圈 while,因為已經走過的就不能再走了,如果不是最短的路,就沒有路可以走了,這樣就能篩選出最短的路。這個設計真是太精妙了!!!請收下我的膝蓋!!!!怎麼可以這麼強,我好興奮啊啊啊啊!!!!
不過,走到 grid[N-1][N-1]
這格時,其實可以 return dis[-1][-1]
而不是 break
,這樣就不用多跑一次 while 迴圈。
拯救我的直覺解
構想:
改良的地方:不要用 stack,改成 queue。從尾巴拿,和從開頭拿,就足以造成失敗或成功的差別,這個領域真是太令人驚嘆了!
python 3 實作(成功):
def shortestPathBinaryMatrix(self, grid: List[List[int]]) -> int:
n = len(grid)
if grid[n-1][n-1] != 0:
return -1
queue = [(0, 0, 0)]
while queue:
i, j, count = queue.pop(0)
if i >= n or j >= n:
continue
if i < 0 or j < 0:
continue
if grid[i][j] != 0:
continue
grid[i][j] = 2
count += 1
if i == n-1 and j == n-1:
return count
queue.extend([(i-1, j-1, count), (i, j-1, count), (i-1, j, count), (i+1, j-1, count), (i-1, j+1, count), (i, j+1, count), (i+1, j, count) ,(i+1, j+1, count)])
return -1
效能:
Runtime: 756 ms, faster than 40.96% of Python3 online submissions for Shortest Path in Binary Matrix.
Memory Usage: 14.7 MB, less than 76.74% of Python3 online submissions for Shortest Path in Binary Matrix.
Comments
Post a Comment