LeetCode Problem Workspace
Detect Cycles in 2D Grid
Detect cycles in a 2D character grid using DFS while carefully tracking parent cells to avoid invalid revisits.
5
Topics
7
Code langs
3
Related
Practice Focus
Medium · Array plus Depth-First Search
Answer-first summary
Detect cycles in a 2D character grid using DFS while carefully tracking parent cells to avoid invalid revisits.
Ace coding interviews with Interview AiBoxInterview AiBox guidance for Array plus Depth-First Search
This problem requires identifying cycles in a 2D grid of characters, ensuring each cycle has length four or more and does not revisit the previous cell immediately. The most efficient approach uses depth-first search while tracking the parent cell to prevent invalid backtracking. This method systematically explores all connected same-value cells, returning true if any valid cycle is found and false otherwise.
Problem Statement
Given a 2D grid of lowercase letters, determine whether there exists a cycle formed by identical letters. A cycle is a path of length four or more that starts and ends at the same cell, moving only to horizontally or vertically adjacent cells of the same value, without revisiting the immediate previous cell.
Implement a function that receives an m x n character grid and returns true if any valid cycle exists, or false if no cycles are present. Ensure your solution handles grids up to size 500 x 500 efficiently, considering all possible same-value paths.
Examples
Example 1
Input: grid = [["a","a","a","a"],["a","b","b","a"],["a","b","b","a"],["a","a","a","a"]]
Output: true
There are two valid cycles shown in different colors in the image below:
Example 2
Input: grid = [["c","c","c","a"],["c","d","c","c"],["c","c","e","c"],["f","c","c","c"]]
Output: true
There is only one valid cycle highlighted in the image below:
Example 3
Input: grid = [["a","b","b"],["b","z","b"],["b","b","a"]]
Output: false
Example details omitted.
Constraints
- m == grid.length
- n == grid[i].length
- 1 <= m, n <= 500
- grid consists only of lowercase English letters.
Solution Approach
DFS with Parent Tracking
Use depth-first search starting from each unvisited cell, recursively exploring neighbors with the same character. Keep track of the parent cell to avoid immediately revisiting the previous position. Return true immediately when a previously visited cell (not the parent) is encountered, indicating a valid cycle.
Iterate Over Entire Grid
Loop through every cell in the grid to ensure no cycle is missed. For each unvisited cell, invoke the DFS routine. This ensures all connected components are checked for cycles and prevents missing isolated cycles in separate areas of the grid.
Early Exit and State Marking
Mark cells as visited during DFS to avoid repeated work. Exit early as soon as a cycle is detected to improve performance. Carefully maintain visited states to differentiate between the current DFS path and previous searches to avoid false positives.
Complexity Analysis
| Metric | Value |
|---|---|
| Time | Depends on the final approach |
| Space | Depends on the final approach |
Time complexity is O(m n) in the worst case because each cell is visited at most once in DFS. Space complexity is O(m n) for the visited matrix and recursion stack for DFS in a fully connected component.
What Interviewers Usually Probe
- They may emphasize handling the 'parent' to prevent invalid 2-cell backtracking.
- Expect questions about time and space trade-offs for DFS versus BFS in grids.
- They might probe how the solution scales when m and n approach 500.
Common Pitfalls or Variants
Common pitfalls
- Failing to track the parent cell can falsely detect a 2-cell back-and-forth as a cycle.
- Not iterating over all unvisited cells may miss cycles in disconnected areas.
- Reusing the visited matrix incorrectly can create false positives or miss valid cycles.
Follow-up variants
- Detect cycles in a 3D grid of characters using the same DFS pattern extended to six directions.
- Find cycles of arbitrary lengths with different movement rules like diagonal adjacency.
- Return the actual cycle path instead of just true/false, using DFS path reconstruction.
FAQ
What is the main strategy to detect cycles in a 2D grid?
Use depth-first search while tracking the parent cell to avoid revisiting the immediate previous cell, returning true if a cycle is found.
Can cycles be diagonal or only horizontal and vertical?
Cycles must only move horizontally or vertically between adjacent same-value cells; diagonals are not considered.
Why is parent tracking critical in this problem?
Without parent tracking, a DFS might consider an immediate back-and-forth as a valid cycle, which violates the length-4 minimum rule.
Does GhostInterview suggest DFS over BFS for this problem?
Yes, DFS is preferred due to natural recursion and parent tracking, making cycle detection straightforward in connected components.
What is the largest grid size this approach can handle efficiently?
The DFS solution with proper visited tracking efficiently handles grids up to 500 x 500, which meets the problem constraints.
Solution
Solution 1: BFS
We can traverse each cell in the 2D grid. For each cell, if the cell $grid[i][j]$ has not been visited, we start a breadth-first search (BFS) from that cell. During the search, we need to record the parent node of each cell and the coordinates of the previous cell. If the value of the next cell is the same as the current cell, and it is not the previous cell, and it has already been visited, then it indicates the presence of a cycle, and we return $\textit{true}$. After traversing all cells, if no cycle is found, we return $\textit{false}$.
class Solution:
def containsCycle(self, grid: List[List[str]]) -> bool:
m, n = len(grid), len(grid[0])
vis = [[False] * n for _ in range(m)]
dirs = (-1, 0, 1, 0, -1)
for i, row in enumerate(grid):
for j, x in enumerate(row):
if vis[i][j]:
continue
vis[i][j] = True
q = [(i, j, -1, -1)]
while q:
x, y, px, py = q.pop()
for dx, dy in pairwise(dirs):
nx, ny = x + dx, y + dy
if 0 <= nx < m and 0 <= ny < n:
if grid[nx][ny] != grid[i][j] or (nx == px and ny == py):
continue
if vis[nx][ny]:
return True
vis[nx][ny] = True
q.append((nx, ny, x, y))
return FalseSolution 2: DFS
We can traverse each cell in the 2D grid. For each cell, if the cell $grid[i][j]$ has not been visited, we start a depth-first search (DFS) from that cell. During the search, we need to record the parent node of each cell and the coordinates of the previous cell. If the value of the next cell is the same as the current cell, and it is not the previous cell, and it has already been visited, then it indicates the presence of a cycle, and we return $\textit{true}$. After traversing all cells, if no cycle is found, we return $\textit{false}$.
class Solution:
def containsCycle(self, grid: List[List[str]]) -> bool:
m, n = len(grid), len(grid[0])
vis = [[False] * n for _ in range(m)]
dirs = (-1, 0, 1, 0, -1)
for i, row in enumerate(grid):
for j, x in enumerate(row):
if vis[i][j]:
continue
vis[i][j] = True
q = [(i, j, -1, -1)]
while q:
x, y, px, py = q.pop()
for dx, dy in pairwise(dirs):
nx, ny = x + dx, y + dy
if 0 <= nx < m and 0 <= ny < n:
if grid[nx][ny] != grid[i][j] or (nx == px and ny == py):
continue
if vis[nx][ny]:
return True
vis[nx][ny] = True
q.append((nx, ny, x, y))
return FalseContinue Topic
array
Practice more edge cases under the same topic.
arrow_forwardauto_awesomeContinue Pattern
Array plus Depth-First Search
Expand the same solving frame across more problems.
arrow_forwardsignal_cellular_altSame Difficulty Track
Medium
Stay on this level to stabilize interview delivery.
arrow_forward