1036. Escape a Large Maze
Problem Description
You have a very large grid with dimensions 1 million by 1 million on an XY-plane. Each square in the grid has coordinates (x, y)
.
You need to determine if you can travel from a starting position source = [sx, sy]
to a destination position target = [tx, ty]
. However, there are some blocked squares that you cannot pass through, given in an array blocked
where each blocked[i] = [xi, yi]
represents a square at coordinates (xi, yi)
that is impassable.
Movement rules:
- From any square, you can move one step in four directions: north (up), east (right), south (down), or west (left)
- You cannot move to a blocked square
- You cannot move outside the grid boundaries (coordinates must stay between 0 and 999,999)
The task is to return true
if there exists at least one valid path from the source to the target following these movement rules, and false
otherwise.
The key insight is that despite the massive grid size, the number of blocked squares is limited (at most 200). This means that blocked squares can only form a barrier of limited size. If blocked squares try to completely encircle either the source or target, they can only trap an area of at most len(blocked)^2 / 2
squares. The solution leverages this by checking if either point can "escape" from a potential encirclement - if both the source and target can reach enough squares (more than the maximum possible trapped area), then they must be able to reach each other.
Flowchart Walkthrough
First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:
Is it a graph?
- Yes: The problem involves a grid where we need to find a path from source to target. A grid can be represented as a graph where each cell is a node and adjacent cells are connected by edges.
Is it a tree?
- No: The grid structure is not a tree. It's a general graph where cycles can exist (you can move in circles).
Is the problem related to directed acyclic graphs?
- No: The grid allows movement in all four directions, creating potential cycles, so it's not a DAG.
Is the problem related to shortest paths?
- No: We only need to determine if a path exists between source and target, not find the shortest one.
Does the problem involve connectivity?
- Yes: The core question is whether the source and target are connected (can we reach target from source).
Does the problem have small constraints?
- Yes: While the grid is massive (10^6 Γ 10^6), the key constraint is that we have at most 200 blocked squares. The solution leverages this small constraint by recognizing that blocked squares can only trap a limited area.
DFS/backtracking?
- Yes: DFS is perfect here because we need to explore paths and the small number of blocked squares means we can efficiently determine connectivity.
Conclusion: The flowchart leads us to use DFS/backtracking. The solution cleverly uses DFS from both source and target, exploiting the small constraint that blocked squares can only trap an area of at most len(blocked)^2 / 2
squares. If either point can explore beyond this limit, it means they've "escaped" any potential encirclement.
Intuition
The key insight comes from understanding what the blocked squares can actually do in such a massive grid. With a 1 million Γ 1 million grid and only up to 200 blocked squares, it's impossible for the blocked squares to create a wall that spans the entire grid.
Think about it this way: if the blocked squares try to form a barrier to trap either the source or target point, what's the maximum area they can enclose? Imagine the most efficient use of blocked squares to create an enclosure - this would be forming something like a diagonal line from the edge of the grid. With n
blocked squares, the maximum area that can be trapped is approximately n^2 / 2
.
This leads to a clever observation: if we start from the source point and can reach more than len(blocked)^2 / 2
unique squares, we must have "escaped" any potential enclosure formed by the blocked squares. Why? Because if we were trapped, we couldn't possibly visit more squares than the maximum area that can be enclosed.
But there's another scenario to consider: what if both source and target are inside the same enclosure? They could reach each other but both would be trapped. Or what if they're in different enclosures? They couldn't reach each other even though neither is technically "stuck" in a small area.
This is why we need to check from both directions:
- Can the source "escape" (reach enough squares) OR reach the target directly?
- Can the target "escape" (reach enough squares) OR reach the source directly?
If both checks pass, it means either:
- Both points are free (not enclosed), so they can definitely reach each other
- Both points are in the same enclosure (since they can reach each other during the DFS)
The beauty of this approach is that we don't need to explore the entire million Γ million grid. We only need to explore up to len(blocked)^2 / 2
squares from each starting point, making the solution feasible despite the enormous grid size.
Learn more about Depth-First Search and Breadth-First Search patterns.
Solution Approach
The implementation uses DFS (Depth-First Search) with a clever early termination condition based on the mathematical insight about the maximum area that can be trapped.
Key Data Structures:
s
: A set containing all blocked positions as tuples for O(1) lookupvis
: A visited set to track explored squares during each DFSdirs
: Direction array(-1, 0, 1, 0, -1)
used withpairwise
to generate the four movement directions
DFS Function Logic:
The dfs
function takes a source position, target position, and a visited set. Here's how it works:
-
Mark Current Position: Add the current source position to the visited set as a tuple
-
Escape Check: If
len(vis) > m
wherem = len(blocked)^2 / 2
, returnTrue
. This means we've explored enough squares to guarantee we're not trapped in an enclosure. -
Explore Neighbors: For each of the four directions (north, east, south, west):
- Calculate new coordinates:
x, y = source[0] + a, source[1] + b
- Check if the new position is:
- Within grid bounds:
0 <= x < n and 0 <= y < n
wheren = 10^6
- Not blocked:
(x, y) not in s
- Not visited:
(x, y) not in vis
- Within grid bounds:
- Calculate new coordinates:
-
Target Check: If the new position equals the target, return
True
(path found) -
Recursive Exploration: Recursively call
dfs
from the new position. If it returnsTrue
, propagate the success. -
No Path Found: If all directions are exhausted without success, return
False
Bidirectional Check:
The solution performs DFS from both directions:
return dfs(source, target, set()) and dfs(target, source, set())
This ensures that:
- The source can either escape its potential enclosure or reach the target
- The target can either escape its potential enclosure or reach the source
Both conditions must be true for a valid path to exist. This handles all cases:
- Both points are free (not enclosed) β both DFS calls succeed
- Both points are in the same enclosure β each can reach the other
- Points are in different enclosures β at least one DFS fails
- One point is enclosed, the other is free β the enclosed point's DFS fails
The algorithm's efficiency comes from the early termination - we never need to explore more than O(len(blocked)^2)
squares, making it practical despite the grid's enormous size.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach. Imagine we have a much smaller 10Γ10 grid (instead of 1 million Γ 1 million) to visualize the concept.
Example Setup:
- Grid size: 10Γ10 (coordinates from 0 to 9)
- Source:
[1, 1]
- Target:
[8, 8]
- Blocked squares:
[[0,2], [1,2], [2,2], [2,1], [2,0]]
(forming an L-shaped barrier)
0 1 2 3 4 5 6 7 8 9 0 . . X . . . . . . . 1 . S X . . . . . . . 2 X X X . . . . . . . 3 . . . . . . . . . . 4 . . . . . . . . . . 5 . . . . . . . . . . 6 . . . . . . . . . . 7 . . . . . . . . . . 8 . . . . . . . . T . 9 . . . . . . . . . . S = Source, T = Target, X = Blocked
Step 1: Calculate Maximum Trapped Area
- Number of blocked squares: 5
- Maximum area that can be trapped:
5^2 / 2 = 12.5
β we'll use 12 - If either DFS can visit more than 12 squares, that point has "escaped"
Step 2: DFS from Source [1,1]
Starting from [1,1]
, the DFS explores:
- Visit
[1,1]
β visited set:{(1,1)}
- Try north
[0,1]
β valid, visit it β visited:{(1,1), (0,1)}
- From
[0,1]
, try west[0,0]
β valid, visit β visited:{(1,1), (0,1), (0,0)}
- Continue exploring...
The DFS would find it's blocked by the L-shaped barrier on certain sides but can escape to the east:
- Path discovered:
[1,1] β [1,0] β [0,0] β [0,1] β ... β [3,1] β [4,1] β ...
- After visiting 13 squares (more than our threshold of 12), the function returns
True
- the source has escaped!
Step 3: DFS from Target [8,8]
Starting from [8,8]
, the DFS explores freely as there are no nearby blocked squares:
- Visit
[8,8]
β visited set:{(8,8)}
- Try north
[7,8]
β valid, visit - Try east
[8,9]
β valid, visit - Continue exploring...
The target quickly reaches 13+ squares since it's in open space, returning True
.
Step 4: Final Result
Both DFS calls return True
:
- Source DFS:
True
(escaped the potential enclosure) - Target DFS:
True
(was never enclosed)
Since both are true, the function returns True
- there exists a path from source to target.
Alternative Scenario - Same Enclosure:
If the blocked squares formed a complete circle around both source and target:
Blocked: forms a 5Γ5 box around both points Source: [3,3] Target: [4,4] . . . . . . . . X X X X X . . X . . . X . . X . S . X . . X . . T X . . X X X X X . . . . . . . .
Here:
- Source DFS would find the target directly before hitting the escape threshold β returns
True
- Target DFS would find the source directly before hitting the escape threshold β returns
True
- Result:
True
(they can reach each other even though both are trapped)
This example demonstrates how the bidirectional check ensures we correctly identify whether a path exists, regardless of whether the points are enclosed or free.
Solution Implementation
1class Solution:
2 def isEscapePossible(
3 self, blocked: List[List[int]], source: List[int], target: List[int]
4 ) -> bool:
5 """
6 Determines if it's possible to reach target from source in a 10^6 x 10^6 grid
7 with certain cells blocked.
8
9 The key insight: if blocked cells form an enclosure, the maximum area they can
10 enclose is approximately (len(blocked))^2 / 2 (when forming a diagonal barrier
11 against a corner). If we can explore more cells than this, we've escaped any
12 potential enclosure.
13
14 Args:
15 blocked: List of blocked cell coordinates
16 source: Starting position [row, col]
17 target: Target position [row, col]
18
19 Returns:
20 True if target is reachable from source, False otherwise
21 """
22
23 def can_escape_or_reach(start_pos: List[int], end_pos: List[int], visited: set) -> bool:
24 """
25 Performs DFS to check if we can either:
26 1. Reach the end_pos from start_pos, or
27 2. Explore enough cells to prove we're not enclosed
28
29 Args:
30 start_pos: Current starting position
31 end_pos: Target position to reach
32 visited: Set of already visited cells
33
34 Returns:
35 True if we can reach end_pos or escape enclosure
36 """
37 # Mark current position as visited
38 visited.add(tuple(start_pos))
39
40 # If we've explored more cells than the maximum possible enclosed area,
41 # we've definitely escaped any enclosure
42 if len(visited) > max_enclosed_area:
43 return True
44
45 # Explore all four directions (up, right, down, left)
46 for i in range(4):
47 # Calculate next position using direction vectors
48 next_row = start_pos[0] + directions[i]
49 next_col = start_pos[1] + directions[i + 1]
50
51 # Check if next position is valid:
52 # 1. Within grid boundaries
53 # 2. Not blocked
54 # 3. Not visited
55 if (0 <= next_row < grid_size and
56 0 <= next_col < grid_size and
57 (next_row, next_col) not in blocked_set and
58 (next_row, next_col) not in visited):
59
60 # Check if we've reached the target
61 if [next_row, next_col] == end_pos:
62 return True
63
64 # Continue DFS from the next position
65 if can_escape_or_reach([next_row, next_col], end_pos, visited):
66 return True
67
68 return False
69
70 # Convert blocked list to set for O(1) lookup
71 blocked_set = {(row, col) for row, col in blocked}
72
73 # Direction vectors for moving up, right, down, left
74 # Using the pattern: (row_delta, col_delta) pairs
75 directions = [-1, 0, 1, 0, -1] # Creates pairs: (-1,0), (0,1), (1,0), (0,-1)
76
77 # Grid size (10^6 x 10^6)
78 grid_size = 10**6
79
80 # Maximum area that can be enclosed by blocked cells
81 # This is approximately (number of blocked cells)^2 / 2
82 # This represents the worst-case scenario where blocked cells form a diagonal
83 max_enclosed_area = len(blocked) ** 2 // 2
84
85 # Check both directions:
86 # 1. Can we reach target from source?
87 # 2. Can we reach source from target?
88 # Both must be true for a valid path to exist
89 return (can_escape_or_reach(source, target, set()) and
90 can_escape_or_reach(target, source, set()))
91
1class Solution {
2 // Grid dimension (1 million x 1 million)
3 private final int GRID_SIZE = (int) 1e6;
4 // Maximum area that can be enclosed by blocked cells
5 private int maxEnclosedArea;
6 // Set to store blocked cell coordinates as encoded values
7 private Set<Long> blockedCells = new HashSet<>();
8 // Direction vectors for moving up, right, down, left
9 private final int[] directions = {-1, 0, 1, 0, -1};
10
11 /**
12 * Determines if it's possible to reach target from source in a grid with blocked cells.
13 * Uses bidirectional search to check if both source and target can escape any enclosure.
14 *
15 * @param blocked Array of blocked cell coordinates
16 * @param source Starting position [x, y]
17 * @param target Destination position [x, y]
18 * @return true if path exists from source to target, false otherwise
19 */
20 public boolean isEscapePossible(int[][] blocked, int[] source, int[] target) {
21 // Store all blocked cells in a set for O(1) lookup
22 for (int[] blockedCell : blocked) {
23 blockedCells.add(encodeCoordinate(blockedCell[0], blockedCell[1]));
24 }
25
26 // Calculate maximum possible enclosed area
27 // With n blocked cells, max area is approximately n*(n-1)/2
28 maxEnclosedArea = blocked.length * blocked.length / 2;
29
30 // Extract source and target coordinates
31 int sourceX = source[0], sourceY = source[1];
32 int targetX = target[0], targetY = target[1];
33
34 // Check both directions: source to target AND target to source
35 // Both must be reachable to ensure no enclosure blocks the path
36 return dfs(sourceX, sourceY, targetX, targetY, new HashSet<>()) &&
37 dfs(targetX, targetY, sourceX, sourceY, new HashSet<>());
38 }
39
40 /**
41 * Performs depth-first search to check if destination is reachable from start.
42 * If visited cells exceed max enclosed area, we've escaped any possible enclosure.
43 *
44 * @param currentX Current x-coordinate
45 * @param currentY Current y-coordinate
46 * @param destinationX Target x-coordinate
47 * @param destinationY Target y-coordinate
48 * @param visitedCells Set of already visited cells in current search
49 * @return true if destination is reachable or we've escaped enclosure
50 */
51 private boolean dfs(int currentX, int currentY, int destinationX, int destinationY,
52 Set<Long> visitedCells) {
53 // If we've visited more cells than max enclosed area, we've escaped
54 if (visitedCells.size() > maxEnclosedArea) {
55 return true;
56 }
57
58 // Try all four directions
59 for (int direction = 0; direction < 4; direction++) {
60 int nextX = currentX + directions[direction];
61 int nextY = currentY + directions[direction + 1];
62
63 // Check if next position is within grid bounds
64 if (nextX >= 0 && nextX < GRID_SIZE && nextY >= 0 && nextY < GRID_SIZE) {
65 // Check if we've reached the destination
66 if (nextX == destinationX && nextY == destinationY) {
67 return true;
68 }
69
70 // Encode the coordinate for efficient storage and lookup
71 long encodedCoordinate = encodeCoordinate(nextX, nextY);
72
73 // Continue DFS if cell is not blocked and not visited
74 if (!blockedCells.contains(encodedCoordinate) &&
75 visitedCells.add(encodedCoordinate)) {
76 if (dfs(nextX, nextY, destinationX, destinationY, visitedCells)) {
77 return true;
78 }
79 }
80 }
81 }
82
83 return false;
84 }
85
86 /**
87 * Encodes 2D coordinates into a single long value for efficient storage.
88 *
89 * @param x X-coordinate
90 * @param y Y-coordinate
91 * @return Encoded coordinate as a long value
92 */
93 private long encodeCoordinate(int x, int y) {
94 return (long) x * GRID_SIZE + y;
95 }
96}
97
1class Solution {
2public:
3 bool isEscapePossible(vector<vector<int>>& blocked, vector<int>& source, vector<int>& target) {
4 // Grid size is 10^6 x 10^6
5 const int GRID_SIZE = 1e6;
6
7 // Maximum area that can be enclosed by blocked cells
8 // With n blocked cells, the maximum enclosed area is approximately n*(n-1)/2
9 int maxEnclosedArea = blocked.size() * blocked.size() / 2;
10
11 // Type alias for long long to handle large coordinates
12 using ll = long long;
13
14 // Set to store blocked cell positions as encoded values
15 unordered_set<ll> blockedSet;
16
17 // Direction vectors for moving up, right, down, left
18 const int directions[5] = {-1, 0, 1, 0, -1};
19
20 // Lambda function to encode 2D coordinates into a single number
21 auto encodePosition = [&](int row, int col) -> ll {
22 return (ll)row * GRID_SIZE + col;
23 };
24
25 // Add all blocked cells to the set for O(1) lookup
26 for (const auto& blockedCell : blocked) {
27 blockedSet.insert(encodePosition(blockedCell[0], blockedCell[1]));
28 }
29
30 // Extract source and target coordinates
31 int sourceRow = source[0], sourceCol = source[1];
32 int targetRow = target[0], targetCol = target[1];
33
34 // Visited sets for both DFS searches
35 unordered_set<ll> visitedFromSource, visitedFromTarget;
36
37 // Recursive DFS lambda function to check if we can reach the target
38 // or if we've explored enough cells to guarantee we're not trapped
39 auto dfs = [&](this auto&& dfs, int currentRow, int currentCol,
40 int destRow, int destCol, unordered_set<ll>& visited) -> bool {
41 // Mark current cell as visited
42 visited.insert(encodePosition(currentRow, currentCol));
43
44 // If we've visited more cells than the maximum possible enclosed area,
45 // we must have escaped any potential enclosure
46 if (visited.size() > maxEnclosedArea) {
47 return true;
48 }
49
50 // Try all four directions
51 for (int k = 0; k < 4; ++k) {
52 int nextRow = currentRow + directions[k];
53 int nextCol = currentCol + directions[k + 1];
54
55 // Check if the next position is within grid bounds
56 if (nextRow >= 0 && nextRow < GRID_SIZE &&
57 nextCol >= 0 && nextCol < GRID_SIZE) {
58
59 // Check if we've reached the destination
60 if (nextRow == destRow && nextCol == destCol) {
61 return true;
62 }
63
64 ll encodedPosition = encodePosition(nextRow, nextCol);
65
66 // Continue DFS if the cell is not blocked and not visited
67 if (!blockedSet.contains(encodedPosition) &&
68 !visited.contains(encodedPosition) &&
69 dfs(nextRow, nextCol, destRow, destCol, visited)) {
70 return true;
71 }
72 }
73 }
74
75 return false;
76 };
77
78 // Check both directions: source to target AND target to source
79 // Both must be reachable to ensure a valid path exists
80 return dfs(sourceRow, sourceCol, targetRow, targetCol, visitedFromSource) &&
81 dfs(targetRow, targetCol, sourceRow, sourceCol, visitedFromTarget);
82 }
83};
84
1/**
2 * Determines if it's possible to reach the target from the source in a grid with blocked cells.
3 * Uses DFS with early termination based on the maximum area that can be enclosed by blocked cells.
4 *
5 * @param blocked - Array of blocked cell coordinates [x, y]
6 * @param source - Starting position [x, y]
7 * @param target - Target position [x, y]
8 * @returns true if escape is possible, false otherwise
9 */
10function isEscapePossible(blocked: number[][], source: number[], target: number[]): boolean {
11 // Grid size (10^6 x 10^6)
12 const GRID_SIZE = 10 ** 6;
13
14 // Maximum area that can be enclosed by blocked cells
15 // Formula: (blockedCount * (blockedCount - 1)) / 2
16 const maxEnclosedArea = (blocked.length ** 2) >> 1;
17
18 // Direction vectors for moving up, right, down, left
19 const directions = [-1, 0, 1, 0, -1];
20
21 // Set to store blocked cell positions as encoded values
22 const blockedCells = new Set<number>();
23
24 /**
25 * Encodes 2D coordinates into a single number for efficient storage
26 * @param row - Row coordinate
27 * @param col - Column coordinate
28 * @returns Encoded position value
29 */
30 const encodePosition = (row: number, col: number): number => {
31 return row * GRID_SIZE + col;
32 };
33
34 // Populate blocked cells set with encoded positions
35 for (const [blockedRow, blockedCol] of blocked) {
36 blockedCells.add(encodePosition(blockedRow, blockedCol));
37 }
38
39 /**
40 * Performs depth-first search to check if target is reachable from start
41 * @param startRow - Starting row position
42 * @param startCol - Starting column position
43 * @param targetRow - Target row position
44 * @param targetCol - Target column position
45 * @param visitedCells - Set of visited cell positions
46 * @returns true if target is reachable, false otherwise
47 */
48 const depthFirstSearch = (
49 startRow: number,
50 startCol: number,
51 targetRow: number,
52 targetCol: number,
53 visitedCells: Set<number>
54 ): boolean => {
55 // Mark current position as visited
56 visitedCells.add(encodePosition(startRow, startCol));
57
58 // If visited cells exceed max possible enclosed area, we've escaped any enclosure
59 if (visitedCells.size > maxEnclosedArea) {
60 return true;
61 }
62
63 // Explore all four directions
64 for (let dirIndex = 0; dirIndex < 4; dirIndex++) {
65 const nextRow = startRow + directions[dirIndex];
66 const nextCol = startCol + directions[dirIndex + 1];
67
68 // Check if next position is within grid boundaries
69 if (nextRow >= 0 && nextRow < GRID_SIZE && nextCol >= 0 && nextCol < GRID_SIZE) {
70 // Check if we've reached the target
71 if (nextRow === targetRow && nextCol === targetCol) {
72 return true;
73 }
74
75 const encodedNextPosition = encodePosition(nextRow, nextCol);
76
77 // Continue DFS if cell is not blocked and not visited
78 if (!blockedCells.has(encodedNextPosition) &&
79 !visitedCells.has(encodedNextPosition) &&
80 depthFirstSearch(nextRow, nextCol, targetRow, targetCol, visitedCells)) {
81 return true;
82 }
83 }
84 }
85
86 return false;
87 };
88
89 // Check both directions: source to target AND target to source
90 // Both must be reachable to ensure neither is trapped in an enclosure
91 return depthFirstSearch(source[0], source[1], target[0], target[1], new Set()) &&
92 depthFirstSearch(target[0], target[1], source[0], source[1], new Set());
93}
94
Time and Space Complexity
Time Complexity: O(m)
where m = len(blocked)^2 / 2
The algorithm performs DFS from both source and target positions. The key insight is that the DFS is bounded by the condition len(vis) > m
. Since m = len(blocked)^2 / 2
, and there are at most 200 blocked cells, we have m β€ 200^2 / 2 = 20,000
.
For each DFS call:
- The recursion explores at most
m + 1
cells before returningTrue
(whenlen(vis) > m
) - Each cell is visited at most once due to the
vis
set - For each visited cell, we check 4 directions, which is
O(1)
- Checking if a position is in the blocked set
s
or visited setvis
takesO(1)
average time for hash set operations
Since we run DFS twice (once from source, once from target), the total time complexity is O(2m) = O(m)
.
Space Complexity: O(m)
where m = len(blocked)^2 / 2
The space is used for:
- The blocked set
s
:O(len(blocked))
which is at mostO(200)
- The visited set
vis
:O(m)
as it stores at mostm + 1
positions - The recursion call stack:
O(m)
in the worst case when DFS goesm
levels deep
The dominant term is O(m)
, so the overall space complexity is O(m)
where m β€ 20,000
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Maximum Enclosed Area Calculation
Pitfall: Using the wrong formula for the maximum area that blocked cells can enclose. A common mistake is assuming blocked cells can form a square perimeter, leading to an incorrect bound like 4 * len(blocked)
or len(blocked)^2
.
Why it happens: The maximum enclosed area occurs when blocked cells form a diagonal barrier against a corner of the grid, creating a triangular region. This gives approximately len(blocked)^2 / 2
cells.
Solution: Use the correct formula:
max_enclosed_area = len(blocked) ** 2 // 2
2. Not Checking Both Directions
Pitfall: Only checking if source can reach target, without verifying the reverse direction:
# Incorrect: Only one direction
return can_escape_or_reach(source, target, set())
Why it fails: Consider this scenario:
- Source is trapped in a small enclosure
- Target is free in the open grid
- The forward DFS from source will quickly return
True
after exploring the small enclosed area (exceeding the threshold) - But source and target are actually unreachable from each other
Solution: Always check both directions:
return (can_escape_or_reach(source, target, set()) and
can_escape_or_reach(target, source, set()))
3. Using List Instead of Set for Blocked Positions
Pitfall: Keeping blocked positions in a list and checking membership with in
:
# Inefficient if [next_row, next_col] in blocked: # O(n) lookup
Why it's problematic: Each lookup takes O(n) time where n is the number of blocked cells. With potentially thousands of lookups during DFS, this creates a significant performance bottleneck.
Solution: Convert to a set of tuples for O(1) lookups:
blocked_set = {(row, col) for row, col in blocked} # Then check: if (next_row, next_col) not in blocked_set: # O(1) lookup
4. Forgetting to Handle Edge Cases
Pitfall: Not considering special cases like:
- Source and target are the same position
- Source or target is in the blocked list
- Empty blocked list
Solution: Add validation at the beginning:
def isEscapePossible(self, blocked, source, target):
# Handle edge cases
if source == target:
return True
if not blocked:
return True
blocked_set = {(row, col) for row, col in blocked}
if tuple(source) in blocked_set or tuple(target) in blocked_set:
return False
# Continue with main algorithm...
5. Stack Overflow with Deep Recursion
Pitfall: Using recursive DFS on a potentially large search space can cause stack overflow, especially in Python which has a default recursion limit.
Why it happens: In worst case, DFS might need to explore up to len(blocked)^2 / 2
cells, which could be 20,000 cells for 200 blocked squares.
Solution: Either increase recursion limit or use iterative DFS:
def can_escape_or_reach_iterative(start_pos, end_pos):
stack = [start_pos]
visited = set()
while stack:
curr = stack.pop()
if curr == end_pos:
return True
visited.add(tuple(curr))
if len(visited) > max_enclosed_area:
return True
for i in range(4):
next_row = curr[0] + directions[i]
next_col = curr[1] + directions[i + 1]
if (0 <= next_row < grid_size and
0 <= next_col < grid_size and
(next_row, next_col) not in blocked_set and
(next_row, next_col) not in visited):
stack.append([next_row, next_col])
return False
Which algorithm should you use to find a node that is close to the root of the tree?
Recommended Readings
https assets algo monster cover_photos dfs svg Depth First Search Prereqs Recursion Review problems recursion_intro Trees problems tree_intro With a solid understanding of recursion under our belts we are now ready to tackle one of the most useful techniques in coding interviews Depth First Search DFS As the name suggests
https assets algo monster cover_photos bfs svg Breadth First Search on Trees Hopefully by this time you've drunk enough DFS Kool Aid to understand its immense power and seen enough visualization to create a call stack in your mind Now let me introduce the companion spell Breadth First Search BFS
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Donβt Miss This!