1030. Matrix Cells in Distance Order
Problem Description
You have a matrix with dimensions rows x cols
, and you start at a specific cell located at coordinates (rCenter, cCenter)
.
Your task is to return a list containing the coordinates of all cells in the matrix, but arranged in a specific order: they must be sorted by their Manhattan distance from your starting position (rCenter, cCenter)
, from smallest to largest distance.
The Manhattan distance between any two cells (r1, c1)
and (r2, c2)
is calculated as: |r1 - r2| + |c1 - c2|
For example, if you have a 3x3 matrix and start at position (1, 1) (the center), the cells would be ordered as:
- Distance 0:
[1, 1]
(the starting cell itself) - Distance 1:
[0, 1]
,[1, 0]
,[1, 2]
,[2, 1]
(cells one step away) - Distance 2:
[0, 0]
,[0, 2]
,[2, 0]
,[2, 2]
(cells two steps away)
When multiple cells have the same distance from the center, they can be returned in any order as long as all cells with the same distance appear together in the correct distance group.
The solution uses a BFS (Breadth-First Search) approach with a queue. Starting from (rCenter, cCenter)
, it explores cells layer by layer - first visiting all cells at distance 1, then distance 2, and so on. A visited array tracks which cells have been processed to avoid duplicates. The pairwise
function with (-1, 0, 1, 0, -1)
generates the four directional movements: up, right, down, and left.
Intuition
When we need to visit all cells sorted by their distance from a starting point, we can think about how distances naturally expand outward like ripples in water. If we drop a stone at (rCenter, cCenter)
, the ripples would reach cells at distance 1 first, then distance 2, then distance 3, and so on.
This pattern immediately suggests BFS (Breadth-First Search) because BFS explores nodes level by level - it visits all nodes at distance k
before visiting any node at distance k+1
. This is exactly what we need!
Starting from our center cell, we can think of the problem as exploring a graph where:
- Each cell is a node
- Each cell is connected to its 4 adjacent neighbors (up, down, left, right)
- Moving from one cell to an adjacent cell increases the Manhattan distance by exactly 1
By using BFS with a queue, we naturally process cells in order of increasing distance:
- Start with the center cell (distance 0)
- Add all unvisited neighbors (distance 1) to the queue
- Process all cells at distance 1, adding their unvisited neighbors (distance 2)
- Continue this pattern until all cells are visited
The key insight is that BFS guarantees we'll visit cells in non-decreasing order of their distance from the starting point. We don't need to explicitly calculate or sort by distances - the BFS traversal order automatically gives us the correct sorting!
We use a vis
(visited) array to ensure each cell is added to our result exactly once. The directional movements (-1, 0, 1, 0, -1)
paired together give us the four directions: up (-1, 0)
, right (0, 1)
, down (1, 0)
, and left (0, -1)
.
Solution Approach
The implementation uses BFS with a queue to traverse the matrix layer by layer from the center point:
1. Initialization:
- Create a queue
q
and initialize it with the starting cell[rCenter, cCenter]
- Create a 2D boolean array
vis
of sizerows x cols
to track visited cells, initially allFalse
- Mark the starting cell as visited:
vis[rCenter][cCenter] = True
- Create an empty list
ans
to store the result
2. BFS Traversal:
while q:
for _ in range(len(q)): # Process all cells at current distance
p = q.popleft() # Get next cell
ans.append(p) # Add to result
The outer while
loop continues until the queue is empty. The inner for
loop processes all cells at the current distance level before moving to the next distance level.
3. Exploring Neighbors:
for a, b in pairwise((-1, 0, 1, 0, -1)): x, y = p[0] + a, p[1] + b
The pairwise
function with (-1, 0, 1, 0, -1)
generates pairs:
(-1, 0)
→ move up(0, 1)
→ move right(1, 0)
→ move down(0, -1)
→ move left
For each direction, we calculate the new coordinates (x, y)
.
4. Boundary Check and Visit:
if 0 <= x < rows and 0 <= y < cols and not vis[x][y]: vis[x][y] = True q.append([x, y])
For each neighbor:
- Check if it's within matrix bounds
- Check if it hasn't been visited
- If both conditions are met, mark it as visited and add to queue
Why This Works:
BFS naturally processes cells in layers:
- Layer 0: Just the center cell (distance 0)
- Layer 1: All cells one step away (distance 1)
- Layer 2: All cells two steps away (distance 2)
- And so on...
Since each move to an adjacent cell increases Manhattan distance by exactly 1, BFS guarantees we visit cells in order of increasing distance. The vis
array prevents revisiting cells, ensuring each cell appears exactly once in the result.
Time Complexity: O(rows × cols)
- we visit each cell exactly once
Space Complexity: O(rows × cols)
- for the visited array and queue
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example with a 3×3 matrix where we start at position (1, 1):
Matrix: [0,0] [0,1] [0,2] [1,0] [1,1] [1,2] [2,0] [2,1] [2,2] Starting position: (1, 1) - the center cell
Initial Setup:
- Queue:
[[1, 1]]
- Visited array: All
False
exceptvis[1][1] = True
- Result:
[]
Round 1 (Distance 0):
- Process
[1, 1]
from queue - Add
[1, 1]
to result - Check 4 neighbors:
- Up:
(0, 1)
- valid, not visited → mark visited, add to queue - Right:
(1, 2)
- valid, not visited → mark visited, add to queue - Down:
(2, 1)
- valid, not visited → mark visited, add to queue - Left:
(1, 0)
- valid, not visited → mark visited, add to queue
- Up:
- Queue:
[[0, 1], [1, 2], [2, 1], [1, 0]]
- Result:
[[1, 1]]
Round 2 (Distance 1): Process all 4 cells currently in queue:
-
Process
[0, 1]
:- Add to result
- Check neighbors:
(0, 0)
and(0, 2)
are unvisited → add to queue
-
Process
[1, 2]
:- Add to result
- Check neighbors:
(0, 2)
already visited,(2, 2)
unvisited → add to queue
-
Process
[2, 1]
:- Add to result
- Check neighbors:
(2, 0)
and(2, 2)
- add(2, 0)
(2,2 already in queue)
-
Process
[1, 0]
:- Add to result
- Check neighbors:
(0, 0)
already in queue,(2, 0)
already in queue
- Queue:
[[0, 0], [0, 2], [2, 2], [2, 0]]
- Result:
[[1, 1], [0, 1], [1, 2], [2, 1], [1, 0]]
Round 3 (Distance 2): Process all 4 corner cells:
- Each cell is processed and added to result
- No new unvisited neighbors (all cells have been visited)
- Queue becomes empty
- Result:
[[1, 1], [0, 1], [1, 2], [2, 1], [1, 0], [0, 0], [0, 2], [2, 2], [2, 0]]
Final Result Verification:
- Distance 0:
[1, 1]
✓ - Distance 1:
[0, 1], [1, 2], [2, 1], [1, 0]
✓ - Distance 2:
[0, 0], [0, 2], [2, 2], [2, 0]
✓
The BFS approach naturally groups cells by their Manhattan distance without explicitly calculating distances!
Solution Implementation
1from collections import deque
2from typing import List
3
4class Solution:
5 def allCellsDistOrder(
6 self, rows: int, cols: int, rCenter: int, cCenter: int
7 ) -> List[List[int]]:
8 """
9 Return all cells in a rows x cols matrix, sorted by their Manhattan distance
10 from (rCenter, cCenter) in non-decreasing order.
11
12 Uses BFS to traverse cells layer by layer from the center.
13 """
14 # Initialize queue with the center cell
15 queue = deque([[rCenter, cCenter]])
16
17 # Create visited matrix to track processed cells
18 visited = [[False] * cols for _ in range(rows)]
19 visited[rCenter][cCenter] = True
20
21 # Result list to store cells in order of distance
22 result = []
23
24 # BFS traversal
25 while queue:
26 # Process all cells at current distance level
27 level_size = len(queue)
28 for _ in range(level_size):
29 current_cell = queue.popleft()
30 result.append(current_cell)
31
32 # Check all 4 adjacent cells (up, right, down, left)
33 # Using pairwise to create direction pairs: (-1,0), (0,1), (1,0), (0,-1)
34 for delta_row, delta_col in [(-1, 0), (0, 1), (1, 0), (0, -1)]:
35 new_row = current_cell[0] + delta_row
36 new_col = current_cell[1] + delta_col
37
38 # Check if the new cell is within bounds and not visited
39 if (0 <= new_row < rows and
40 0 <= new_col < cols and
41 not visited[new_row][new_col]):
42 visited[new_row][new_col] = True
43 queue.append([new_row, new_col])
44
45 return result
46
1class Solution {
2 public int[][] allCellsDistOrder(int rows, int cols, int rCenter, int cCenter) {
3 // Initialize BFS queue with the center cell
4 Deque<int[]> queue = new ArrayDeque<>();
5 queue.offer(new int[] {rCenter, cCenter});
6
7 // Track visited cells to avoid duplicates
8 boolean[][] visited = new boolean[rows][cols];
9 visited[rCenter][cCenter] = true;
10
11 // Result array to store all cells in order of distance
12 int[][] result = new int[rows * cols][2];
13
14 // Direction vectors for moving up, right, down, left
15 // Using the pattern: (row-1, col), (row, col+1), (row+1, col), (row, col-1)
16 int[] directions = {-1, 0, 1, 0, -1};
17
18 // Index to track position in result array
19 int resultIndex = 0;
20
21 // BFS traversal - cells are naturally ordered by distance
22 while (!queue.isEmpty()) {
23 // Process all cells at current distance level
24 int levelSize = queue.size();
25 for (int i = 0; i < levelSize; i++) {
26 // Get current cell coordinates
27 int[] currentCell = queue.poll();
28
29 // Add current cell to result
30 result[resultIndex++] = currentCell;
31
32 // Explore all 4 adjacent cells
33 for (int k = 0; k < 4; k++) {
34 int newRow = currentCell[0] + directions[k];
35 int newCol = currentCell[1] + directions[k + 1];
36
37 // Check if the new cell is within bounds and not visited
38 if (newRow >= 0 && newRow < rows &&
39 newCol >= 0 && newCol < cols &&
40 !visited[newRow][newCol]) {
41
42 // Mark as visited and add to queue
43 visited[newRow][newCol] = true;
44 queue.offer(new int[] {newRow, newCol});
45 }
46 }
47 }
48 }
49
50 return result;
51 }
52}
53
1class Solution {
2public:
3 vector<vector<int>> allCellsDistOrder(int rows, int cols, int rCenter, int cCenter) {
4 // Initialize BFS queue with the center cell
5 queue<pair<int, int>> bfsQueue;
6 bfsQueue.emplace(rCenter, cCenter);
7
8 // Result vector to store cells in order of increasing distance
9 vector<vector<int>> result;
10
11 // Visited matrix to track which cells have been processed
12 bool visited[rows][cols];
13 memset(visited, false, sizeof(visited));
14 visited[rCenter][cCenter] = true;
15
16 // Direction vectors for moving up, right, down, left
17 // Using the pattern: (row_offset[i], col_offset[i]) for i = 0 to 3
18 int directionOffsets[5] = {-1, 0, 1, 0, -1};
19
20 // BFS traversal to visit cells level by level (same distance cells together)
21 while (!bfsQueue.empty()) {
22 // Process all cells at current distance level
23 int currentLevelSize = bfsQueue.size();
24 for (int i = 0; i < currentLevelSize; ++i) {
25 // Get and remove the front cell from queue
26 auto [currentRow, currentCol] = bfsQueue.front();
27 bfsQueue.pop();
28
29 // Add current cell to result
30 result.push_back({currentRow, currentCol});
31
32 // Explore all 4 adjacent cells
33 for (int direction = 0; direction < 4; ++direction) {
34 int nextRow = currentRow + directionOffsets[direction];
35 int nextCol = currentCol + directionOffsets[direction + 1];
36
37 // Check if the adjacent cell is valid and unvisited
38 if (nextRow >= 0 && nextRow < rows &&
39 nextCol >= 0 && nextCol < cols &&
40 !visited[nextRow][nextCol]) {
41 // Mark as visited and add to queue for next level
42 visited[nextRow][nextCol] = true;
43 bfsQueue.emplace(nextRow, nextCol);
44 }
45 }
46 }
47 }
48
49 return result;
50 }
51};
52
1function allCellsDistOrder(rows: number, cols: number, rCenter: number, cCenter: number): number[][] {
2 // Initialize BFS queue with the center cell
3 const bfsQueue: [number, number][] = [];
4 bfsQueue.push([rCenter, cCenter]);
5
6 // Result array to store cells in order of increasing distance
7 const result: number[][] = [];
8
9 // Visited matrix to track which cells have been processed
10 const visited: boolean[][] = Array(rows).fill(null).map(() => Array(cols).fill(false));
11 visited[rCenter][cCenter] = true;
12
13 // Direction vectors for moving up, right, down, left
14 // Using the pattern: (rowOffset[i], colOffset[i]) for i = 0 to 3
15 const directionOffsets: number[] = [-1, 0, 1, 0, -1];
16
17 // BFS traversal to visit cells level by level (same distance cells together)
18 while (bfsQueue.length > 0) {
19 // Process all cells at current distance level
20 const currentLevelSize = bfsQueue.length;
21 for (let i = 0; i < currentLevelSize; i++) {
22 // Get and remove the front cell from queue
23 const [currentRow, currentCol] = bfsQueue.shift()!;
24
25 // Add current cell to result
26 result.push([currentRow, currentCol]);
27
28 // Explore all 4 adjacent cells
29 for (let direction = 0; direction < 4; direction++) {
30 const nextRow = currentRow + directionOffsets[direction];
31 const nextCol = currentCol + directionOffsets[direction + 1];
32
33 // Check if the adjacent cell is valid and unvisited
34 if (nextRow >= 0 && nextRow < rows &&
35 nextCol >= 0 && nextCol < cols &&
36 !visited[nextRow][nextCol]) {
37 // Mark as visited and add to queue for next level
38 visited[nextRow][nextCol] = true;
39 bfsQueue.push([nextRow, nextCol]);
40 }
41 }
42 }
43 }
44
45 return result;
46}
47
Time and Space Complexity
Time Complexity: O(rows * cols)
The algorithm uses BFS (Breadth-First Search) to traverse all cells in the matrix starting from the center position. Each cell is visited exactly once due to the visited array vis
. For each cell, we perform constant-time operations:
- Dequeuing from the queue:
O(1)
- Appending to the answer list:
O(1)
- Checking 4 adjacent cells using pairwise iteration:
O(4) = O(1)
- Marking cells as visited and enqueuing:
O(1)
Since we visit all rows * cols
cells exactly once, the total time complexity is O(rows * cols)
.
Space Complexity: O(rows * cols)
The space complexity consists of:
- The
vis
(visited) array:O(rows * cols)
to track which cells have been visited - The queue
q
: In the worst case, the queue can containO(rows * cols)
elements (though in practice it's much smaller due to BFS level-by-level processing) - The answer array
ans
:O(rows * cols)
to store all cell coordinates - The temporary list
p
and variables:O(1)
The dominant factor is the space needed for the visited array and the answer array, giving us a total space complexity of O(rows * cols)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Not Processing Cells Level by Level
A critical mistake is forgetting to process all cells at the current distance before moving to the next distance level. Without the inner loop that processes len(queue)
cells, the BFS would mix cells from different distances:
Incorrect Implementation:
while queue: current_cell = queue.popleft() result.append(current_cell) # Add neighbors to queue...
This would process cells in the order they're added to the queue, not strictly by distance layers.
Correct Implementation:
while queue:
level_size = len(queue)
for _ in range(level_size): # Process all cells at current distance
current_cell = queue.popleft()
result.append(current_cell)
# Add neighbors to queue...
2. Marking Cells as Visited After Dequeuing
A common BFS mistake is marking cells as visited when they're dequeued rather than when they're enqueued. This causes duplicate cells in the queue:
Incorrect Pattern:
while queue: current_cell = queue.popleft() if visited[current_cell[0]][current_cell[1]]: continue visited[current_cell[0]][current_cell[1]] = True # Too late! result.append(current_cell)
This allows the same cell to be added to the queue multiple times from different neighbors, leading to:
- Duplicate cells in the result
- Inefficient processing
- Potentially incorrect ordering
Correct Pattern:
if not visited[new_row][new_col]: visited[new_row][new_col] = True # Mark immediately when adding queue.append([new_row, new_col])
3. Using Wrong Distance Calculation
Some might try to optimize by calculating actual Manhattan distances and sorting, which is unnecessary and error-prone:
Overcomplicated Approach:
all_cells = []
for r in range(rows):
for c in range(cols):
dist = abs(r - rCenter) + abs(c - cCenter)
all_cells.append((dist, [r, c]))
all_cells.sort()
return [cell for _, cell in all_cells]
While this works, it's less efficient (O(n log n) sorting) and doesn't leverage the natural ordering that BFS provides.
4. Incorrect Direction Arrays
Using inconsistent or incorrect direction arrays can miss cells or cause index errors:
Common Mistakes:
# Mistake 1: Only 2 directions directions = [(0, 1), (1, 0)] # Missing up and left # Mistake 2: Diagonal movements (wrong for Manhattan distance) directions = [(1, 1), (-1, -1), (1, -1), (-1, 1)] # Mistake 3: Typos in coordinates directions = [(1, 0), (0, 1), (-1, 0), (0, 1)] # Duplicate (0,1)
Correct Implementation:
directions = [(-1, 0), (0, 1), (1, 0), (0, -1)] # All 4 cardinal directions
Which data structure is used to implement recursion?
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
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!