{x}
blog image

Escape a Large Maze

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
  • It is guaranteed that source and target are not blocked.

Solution Explanation for Escape a Large Maze

This problem asks whether it's possible to reach a target point from a source point on a large grid, given a set of blocked cells. A brute-force approach would be computationally expensive. The solution employs a clever optimization using Depth-First Search (DFS) or Breadth-First Search (BFS) with a visit limit.

Core Idea:

The key insight is that if we haven't encountered a solution after exploring a certain number of cells (set to 20000 in the provided solutions), it's highly unlikely that a path exists. This is because the grid is vast (1 million x 1 million), and a path that requires significantly more steps to reach the target is improbable given the relatively small number of blocked cells. This significantly reduces the search space and avoids excessive computation.

Algorithm (DFS/BFS):

  1. Data Structures: Use a HashSet (or equivalent) to efficiently track visited cells and blocked cells. For large-scale problems, a hash table's average O(1) lookup time is crucial for performance.
  2. DFS (or BFS): Perform a DFS or BFS starting from the source.
  3. Visit Limit: Keep track of the number of visited cells. If it exceeds the pre-defined limit (e.g., 20000), assume that the target is unreachable and return false.
  4. Target Found: If the target is reached during the search, return true.
  5. Bi-directional Search: For improved efficiency, the optimal solutions perform a bi-directional search. They run a search from source to target and another search from target to source. If both searches find a path within the limit, then there is a path. This is because if a path exists, it will be found by either one of the searches.

Time Complexity Analysis:

  • Worst Case: O(B + V), where B is the number of blocked cells, and V is the number of visited cells during the search. In the worst case, V could approach the visit limit (20000) for each direction of the bidirectional search. However, it is highly unlikely to reach this limit. The algorithm is significantly faster than the naive O(N^2) approach for exploring the full grid.
  • Average Case: The average-case complexity is much lower due to the visit limit. This heuristic dramatically reduces search space. The time complexity will be closer to O(min(B, V)), which is close to linear in most practical scenarios because the number of visited cells will be far smaller than the size of the grid.

Space Complexity:

O(V), where V is the number of visited cells. Again, V is bounded by the visit limit in most cases, making the space complexity essentially constant for the vast majority of cases.

Code Explanation (Python Example):

def isEscapePossible(blocked, source, target):
    blocked_set = set((x, y) for x, y in blocked)  # Efficient lookup
 
    def dfs(start, end, visited):
        x, y = start
        if (x, y) in visited or not (0 <= x < 10**6 and 0 <= y < 10**6) or (x, y) in blocked_set:
            return False
        visited.add((x, y))
        if len(visited) > 20000 or (x, y) == (end[0], end[1]):
            return True
        for dx, dy in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
            if dfs((x + dx, y + dy), end, visited):
                return True
        return False
 
    return dfs(source, target, set()) and dfs(target, source, set())

The Python code efficiently uses sets for fast lookups, making the search process quite efficient, especially considering the large size of the grid. The Java, C++, Go, and Rust solutions all implement similar strategies, utilizing their language's hash table equivalents and optimizing for the large input size. The use of the visit limit makes this solution practical and efficient for the problem's constraints.