150NeetCode 150

137.Spiral Matrix

MediumBlind 75Math & Geometry ยท 2/8

Problem

Given an m x n matrix, return all of its elements in spiral order: start at the top-left, go right along the top row, down the right column, left along the bottom row, up the left column, and keep spiralling inward until every element has been visited.

Examples

Input:  matrix = [[1,2,3],
                  [4,5,6],
                  [7,8,9]]
Output: [1,2,3,6,9,8,7,4,5]
Input:  matrix = [[1,2,3,4],
                  [5,6,7,8],
                  [9,10,11,12]]
Output: [1,2,3,4,8,12,11,10,9,5,6,7]

Constraints

  • m == matrix.length, n == matrix[i].length
  • 1 <= m, n <= 10
  • -100 <= matrix[i][j] <= 100

๐Ÿ’ก Key Insight

The spiral is a sequence of shrinking rectangle boundaries. Keep four walls โ€” top, bottom, left, right โ€” and after walking each side, move that wall inward by one. The only subtlety is non-square matrices: after the top row and right column, the remaining rectangle may already be empty, so check top <= bottom and left <= right before walking the bottom row and left column.

Simulation with Visited Array

TimeO(m ยท n)SpaceO(m ยท n)

Intuition

Simulate a walker. It moves in direction order right โ†’ down โ†’ left โ†’ up. When the next cell is outside the matrix or already visited, it turns clockwise. After m ยท n steps every cell has been collected.

Algorithm

  1. dr = {0, 1, 0, -1}, dc = {1, 0, -1, 0}; start at (0, 0), direction 0.
  2. Repeat m ยท n times:
    • Add matrix[r][c], mark visited.
    • Compute the next cell; if it is invalid or visited, turn: d = (d + 1) % 4.
    • Move to the next cell in direction d.

Visualization

1 โ†’ 2 โ†’ 3
        โ†“
4 โ†’ 5   6      the walker turns at the wall (3), at the wall (9),
โ†‘       โ†“      at the wall (7), and at the visited cell (1),
7 โ† 8 โ† 9      then at visited 6 after reaching 5.

output: 1 2 3 6 9 8 7 4 5

Code

java
import java.util.*;

class Solution {
    public List<Integer> spiralOrder(int[][] matrix) {
        int m = matrix.length, n = matrix[0].length;
        int[] dr = {0, 1, 0, -1}; // right, down, left, up
        int[] dc = {1, 0, -1, 0};
        boolean[][] visited = new boolean[m][n];
        List<Integer> result = new ArrayList<>();
        int r = 0, c = 0, d = 0;
        for (int step = 0; step < m * n; step++) {
            result.add(matrix[r][c]);
            visited[r][c] = true;
            int nr = r + dr[d], nc = c + dc[d];
            if (nr < 0 || nr >= m || nc < 0 || nc >= n || visited[nr][nc]) {
                d = (d + 1) % 4; // blocked: turn clockwise
                nr = r + dr[d];
                nc = c + dc[d];
            }
            r = nr;
            c = nc;
        }
        return result;
    }
}

Complexity

  • Time: O(m ยท n) โ€” one step per cell.
  • Space: O(m ยท n) โ€” the visited array.

Complexity Comparison

Approach Time Space (extra)
Simulation + visited O(m ยท n) O(m ยท n)
Boundaries O(m ยท n) O(1)

Common Pitfalls

  • Omitting the top <= bottom / left <= right checks โ€” a single remaining row (e.g. [[1,2,3]]) gets read twice, forward and backward.
  • Updating a wall before its loop instead of after it, shifting the walked row/column by one.
  • Assuming the matrix is square โ€” test 1 x n, m x 1 and 3 x 4 shapes.
  • In the simulation, turning only on out-of-bounds and forgetting the visited check โ€” the walker re-enters the outer ring.