Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
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 <= 200blocked[i].length == 20 <= xi, yi < 106source.length == target.length == 20 <= sx, sy, tx, ty < 106source != targetsource and target are not blocked.Problem summary: 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.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Hash Map
[[0,1],[1,0]] [0,0] [0,2]
[] [0,0] [999999,999999]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #1036: Escape a Large Maze
class Solution {
private final int n = (int) 1e6;
private int m;
private Set<Long> s = new HashSet<>();
private final int[] dirs = {-1, 0, 1, 0, -1};
public boolean isEscapePossible(int[][] blocked, int[] source, int[] target) {
for (var b : blocked) {
s.add(f(b[0], b[1]));
}
m = blocked.length * blocked.length / 2;
int sx = source[0], sy = source[1];
int tx = target[0], ty = target[1];
return dfs(sx, sy, tx, ty, new HashSet<>()) && dfs(tx, ty, sx, sy, new HashSet<>());
}
private boolean dfs(int sx, int sy, int tx, int ty, Set<Long> vis) {
if (vis.size() > m) {
return true;
}
for (int k = 0; k < 4; ++k) {
int x = sx + dirs[k], y = sy + dirs[k + 1];
if (x >= 0 && x < n && y >= 0 && y < n) {
if (x == tx && y == ty) {
return true;
}
long key = f(x, y);
if (!s.contains(key) && vis.add(key) && dfs(x, y, tx, ty, vis)) {
return true;
}
}
}
return false;
}
private long f(int i, int j) {
return (long) i * n + j;
}
}
// Accepted solution for LeetCode #1036: Escape a Large Maze
func isEscapePossible(blocked [][]int, source []int, target []int) bool {
const n = 1_000_000
m := len(blocked) * len(blocked) / 2
dirs := [5]int{-1, 0, 1, 0, -1}
f := func(i, j int) int64 {
return int64(i*n + j)
}
s := make(map[int64]bool)
for _, b := range blocked {
s[f(b[0], b[1])] = true
}
var dfs func(sx, sy, tx, ty int, vis map[int64]bool) bool
dfs = func(sx, sy, tx, ty int, vis map[int64]bool) bool {
key := f(sx, sy)
vis[key] = true
if len(vis) > m {
return true
}
for k := 0; k < 4; k++ {
x, y := sx+dirs[k], sy+dirs[k+1]
if x >= 0 && x < n && y >= 0 && y < n {
if x == tx && y == ty {
return true
}
key := f(x, y)
if !s[key] && !vis[key] && dfs(x, y, tx, ty, vis) {
return true
}
}
}
return false
}
sx, sy := source[0], source[1]
tx, ty := target[0], target[1]
return dfs(sx, sy, tx, ty, map[int64]bool{}) && dfs(tx, ty, sx, sy, map[int64]bool{})
}
# Accepted solution for LeetCode #1036: Escape a Large Maze
class Solution:
def isEscapePossible(
self, blocked: List[List[int]], source: List[int], target: List[int]
) -> bool:
def dfs(source: List[int], target: List[int], vis: set) -> bool:
vis.add(tuple(source))
if len(vis) > m:
return True
for a, b in pairwise(dirs):
x, y = source[0] + a, source[1] + b
if 0 <= x < n and 0 <= y < n and (x, y) not in s and (x, y) not in vis:
if [x, y] == target or dfs([x, y], target, vis):
return True
return False
s = {(x, y) for x, y in blocked}
dirs = (-1, 0, 1, 0, -1)
n = 10**6
m = len(blocked) ** 2 // 2
return dfs(source, target, set()) and dfs(target, source, set())
// Accepted solution for LeetCode #1036: Escape a Large Maze
use std::collections::HashSet;
impl Solution {
pub fn is_escape_possible(blocked: Vec<Vec<i32>>, source: Vec<i32>, target: Vec<i32>) -> bool {
const N: i64 = 1_000_000;
let m = (blocked.len() * blocked.len()) as i64 / 2;
let f = |i: i64, j: i64| -> i64 { i * N + j };
let mut s: HashSet<i64> = HashSet::new();
for b in &blocked {
s.insert(f(b[0] as i64, b[1] as i64));
}
fn dfs(
sx: i64,
sy: i64,
tx: i64,
ty: i64,
s: &HashSet<i64>,
m: i64,
vis: &mut HashSet<i64>,
) -> bool {
static DIRS: [i64; 5] = [-1, 0, 1, 0, -1];
let key = sx * 1_000_000 + sy;
vis.insert(key);
if vis.len() as i64 > m {
return true;
}
for k in 0..4 {
let x = sx + DIRS[k];
let y = sy + DIRS[k + 1];
let key = x * 1_000_000 + y;
if x >= 0 && x < 1_000_000 && y >= 0 && y < 1_000_000 {
if x == tx && y == ty {
return true;
}
if !s.contains(&key) && vis.insert(key) && dfs(x, y, tx, ty, s, m, vis) {
return true;
}
}
}
false
}
dfs(
source[0] as i64,
source[1] as i64,
target[0] as i64,
target[1] as i64,
&s,
m,
&mut HashSet::new(),
) && dfs(
target[0] as i64,
target[1] as i64,
source[0] as i64,
source[1] as i64,
&s,
m,
&mut HashSet::new(),
)
}
}
// Accepted solution for LeetCode #1036: Escape a Large Maze
function isEscapePossible(blocked: number[][], source: number[], target: number[]): boolean {
const n = 10 ** 6;
const m = (blocked.length ** 2) >> 1;
const dirs = [-1, 0, 1, 0, -1];
const s = new Set<number>();
const f = (i: number, j: number): number => i * n + j;
for (const [x, y] of blocked) {
s.add(f(x, y));
}
const dfs = (sx: number, sy: number, tx: number, ty: number, vis: Set<number>): boolean => {
vis.add(f(sx, sy));
if (vis.size > m) {
return true;
}
for (let k = 0; k < 4; k++) {
const x = sx + dirs[k],
y = sy + dirs[k + 1];
if (x >= 0 && x < n && y >= 0 && y < n) {
if (x === tx && y === ty) {
return true;
}
const key = f(x, y);
if (!s.has(key) && !vis.has(key) && dfs(x, y, tx, ty, vis)) {
return true;
}
}
}
return false;
};
return (
dfs(source[0], source[1], target[0], target[1], new Set()) &&
dfs(target[0], target[1], source[0], source[1], new Set())
);
}
Use this to step through a reusable interview workflow for this problem.
Two nested loops check every pair or subarray. The outer loop fixes a starting point, the inner loop extends or searches. For n elements this gives up to n²/2 operations. No extra space, but the quadratic time is prohibitive for large inputs.
Most array problems have an O(n²) brute force (nested loops) and an O(n) optimal (single pass with clever state tracking). The key is identifying what information to maintain as you scan: a running max, a prefix sum, a hash map of seen values, or two pointers.
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.