|
1 | 1 | # 1091. Shortest Path in Binary Matrix
|
2 | 2 |
|
3 | 3 | ## BFS Solution
|
4 |
| -- Runtime: O(N) |
| 4 | +- Run-time: O(N) |
5 | 5 | - Space: O(N)
|
6 | 6 | - N = Number of elements in grid
|
7 | 7 |
|
@@ -33,29 +33,70 @@ class Solution:
|
33 | 33 | bfs_queue.append((_x, _y))
|
34 | 34 | grid[_x][_y] = -1
|
35 | 35 | return -1
|
36 |
| - |
| 36 | +
|
37 | 37 | def get_neighbors(self, grid, x, y):
|
38 | 38 | axises = [(1,0),(0,1),(0,-1),(-1,0),(-1,-1),(1,-1),(-1,1),(1,1)]
|
39 | 39 | for _x, _y in axises:
|
40 | 40 | _x += x
|
41 | 41 | _y += y
|
42 | 42 | if self.within_bounds(_x, _y, grid) and grid[_x][_y] == 0:
|
43 | 43 | yield (_x, _y)
|
44 |
| - |
| 44 | +
|
45 | 45 | def within_bounds(self, x, y, grid):
|
46 | 46 | return x >= 0 and x < len(grid) and y >= 0 and y < len(grid[0])
|
47 | 47 | ```
|
48 | 48 |
|
49 | 49 | ## Bi-directional BFS Best Solution
|
50 |
| -- Runtime: O(N) |
| 50 | +- Run-time: O(N) |
51 | 51 | - Space: O(N)
|
52 | 52 | - N = Number of elements in grid
|
53 | 53 |
|
54 | 54 | We can further improve the run-time of the BFS by using a bi-directional BFS.
|
55 | 55 | If you imagine a circle, every time you expand the circle, the number of neighbors being visited almost doubles.
|
56 | 56 | Now if you used two circles and expanded them both simultaneously, when they touch, it would have visited less neighbors compared to using one circle.
|
57 |
| -This increases the run-time even more for some inputs, worst case, is the same as the first BFS solution. |
| 57 | +This decreases the run-time for some inputs, worst case, is the same as the first BFS solution. |
58 | 58 |
|
59 | 59 | ```
|
| 60 | +from collections import deque |
| 61 | +
|
| 62 | +class Solution(object): |
| 63 | + def shortestPathBinaryMatrix(self, grid): |
| 64 | +
|
| 65 | + def get_neighbors(row_idx, col_idx): |
| 66 | + dirs = [(0,1),(1,0),(0,-1),(-1,0),(1,1),(-1,-1),(-1,1),(1,-1)] |
| 67 | + for r, c in dirs: |
| 68 | + r += row_idx |
| 69 | + c += col_idx |
| 70 | + if 0 <= r < len(grid) and 0 <= c < len(grid[0]): |
| 71 | + yield (r, c) |
60 | 72 |
|
| 73 | + def bfs_once(queue, our_num, other_num): |
| 74 | + for _ in range(len(queue)): |
| 75 | + node = queue.pop() |
| 76 | + for x, y in get_neighbors(node[0], node[1]): |
| 77 | + if grid[x][y] == other_num: # intersection |
| 78 | + return True |
| 79 | + elif grid[x][y] == 0: |
| 80 | + grid[x][y] = our_num |
| 81 | + queue.appendleft((x, y)) |
| 82 | + return False |
| 83 | +
|
| 84 | + if not grid or len(grid) == 0 or len(grid[0]) == 0: # is grid empty? |
| 85 | + return -1 |
| 86 | + elif len(grid) == 1 and len(grid[0]) == 1: # does grid only contain one element? |
| 87 | + return 1 |
| 88 | + elif grid[0][0] == 1 or grid[-1][-1] == 1: # are start or end blocked? |
| 89 | + return -1 |
| 90 | + queue1 = deque([(0, 0)]) |
| 91 | + queue2 = deque([(len(grid)-1, len(grid[0])-1)]) |
| 92 | + grid[0][0], grid[-1][-1] = 2, 3 # numbers represent two visited sets |
| 93 | + n_moves = 2 |
| 94 | + while queue1 and queue2: |
| 95 | + if bfs_once(queue1, 2, 3): |
| 96 | + return n_moves |
| 97 | + n_moves += 1 |
| 98 | + if bfs_once(queue2, 3, 2): |
| 99 | + return n_moves |
| 100 | + n_moves += 1 |
| 101 | + return -1 |
61 | 102 | ```
|
0 commit comments