There is a 1 million by 1 million grid on an XY-plane, and the coordinates of each grid square are (x, y)
.
We start at the source = [sx, sy]
square and want to reach the target = [tx, ty]
square. There is also an array of blocked
squares, where each blocked[i] = [xi, yi]
represents a blocked square with coordinates (xi, yi)
.
Each move, we can walk one square north, east, south, or west if the square is not in the array of blocked
squares. We are also not allowed to walk outside of the grid.
Return true
if and only if it is possible to reach the target
square from the source
square through a sequence of valid moves.
Example 1:
Input: blocked = [[0,1],[1,0]], source = [0,0], target = [0,2] Output: false Explanation: The target square is inaccessible starting from the source square because we cannot move. We cannot move north or east because those squares are blocked. We cannot move south or west because we cannot go outside of the grid.
Example 2:
Input: blocked = [], source = [0,0], target = [999999,999999] Output: true Explanation: Because there are no blocked cells, it is possible to reach the target square.
Constraints:
0 <= blocked.length <= 200
blocked[i].length == 2
0 <= xi, yi < 106
source.length == target.length == 2
0 <= sx, sy, tx, ty < 106
source != target
source
and target
are not blocked.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force method to escape a maze is like trying every single path until you find one that leads out. We explore all possible directions step-by-step, without being smart about which way to go.
Here's how the algorithm would work step-by-step:
def escape_maze_brute_force(maze, start_row, start_column, end_row, end_column):
rows = len(maze)
columns = len(maze[0])
visited = set()
queue = [(start_row, start_column, [])]
while queue:
current_row, current_column, path = queue.pop(0)
if (current_row, current_column) == (end_row, end_column):
return path + [(current_row, current_column)]
if (current_row, current_column) in visited:
continue
visited.add((current_row, current_column))
# Explore possible directions
possible_moves = [
(current_row - 1, current_column), # Up
(current_row + 1, current_column), # Down
(current_row, current_column - 1), # Left
(current_row, current_column + 1) # Right
]
for next_row, next_column in possible_moves:
# Make sure to stay inside the maze
if 0 <= next_row < rows and 0 <= next_column < columns and \
maze[next_row][next_column] == 0:
# Add valid moves to the queue to explore
queue.append((next_row, next_column, path + [(current_row, current_column)]))
return None
The key to efficiently escaping a large maze is to avoid exploring every possible path. Instead, we'll use a technique that focuses on gradually building a path to the destination, prioritizing expanding outwards from both the start and the target simultaneously to meet in the middle.
Here's how the algorithm would work step-by-step:
def is_escape_possible(blocked, source_point, target_point, max_steps = 20000): blocked_set = set(map(tuple, blocked))
# If source or target is blocked, escape is impossible
if tuple(source_point) in blocked_set or tuple(target_point) in blocked_set:
return False
def explore(start_row, start_col, target_row, target_col, blocked_set): visited = set()
queue = [(start_row, start_col)]
visited.add((start_row, start_col))
# Limit exploration to avoid infinite loops in enclosed areas
for _ in range(max_steps):
if not queue:
return False
current_row, current_col = queue.pop(0)
if abs(current_row - target_row) + abs(current_col - target_col) <= 1:
return True
for row_direction, col_direction in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
new_row, new_col = current_row + row_direction, current_col + col_direction
if (new_row, new_col) not in visited and \
0 <= new_row <= 10**6 - 1 and 0 <= new_col <= 10**6 - 1 and \
(new_row, new_col) not in blocked_set:
queue.append((new_row, new_col))
visited.add((new_row, new_col))
return True
# Explore from source towards target and vice versa
return explore(source_point[0], source_point[1], target_point[0], target_point[1], blocked_set) and \
explore(target_point[0], target_point[1], source_point[0], source_point[1], blocked_set)
Case | How to Handle |
---|---|
Maze is null or empty | Return false immediately as there's no maze to escape from, indicating escape impossible. |
Start or target is out of bounds | Return false immediately since the starting or target position is invalid. |
Start and target are the same cell | Return true immediately because no path is needed; escape is already achieved. |
Maze is fully blocked (all cells are walls) | Return false immediately as there is no possible path to escape. |
Maze dimensions are very large (potential memory issues) | Consider using an iterative BFS approach with limited queue size or A* to manage memory usage, preventing OOM errors. |
Start or target is a wall | Return false immediately because you cannot start or end on a blocked cell. |
Integer overflow when calculating distances or coordinates | Use appropriate data types (e.g., long) to store distances or coordinates, or handle calculations modulo a large prime number. |
Cycles in the search path (infinite loop) | Maintain a 'visited' set to prevent revisiting the same cell, ensuring termination and correct result. |