Skip to main content

Spiral Matrix

How to walk a matrix in layers using clear top bottom left right boundaries instead of scattered conditions.

Given a matrix matrix, return all elements in spiral order.

Example:

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

Output: [1,2,3,6,9,8,7,4,5]

What to notice before coding

Common mistakes

Step 1 - Understand the traversal by layers

Spiral order is not random.

It walks:

  • the outer border
  • then the next inner border
  • and so on

This is a layer-by-layer traversal.

Step 2 - Write the most direct baseline

The natural baseline is:

  • keep the current direction
  • keep a visited matrix
  • turn when you hit a boundary or a visited cell

It works.

But it carries more state than necessary.

Step 3 - Ask what state really matters

For the current layer, it is enough to know:

  • top
  • bottom
  • left
  • right

Those four boundaries describe exactly the rectangle that is still unvisited.

Step 4 - Walk and shrink

In each round:

  • walk the top
  • walk the right
  • if a row still remains, walk the bottom
  • if a column still remains, walk the left

Then shrink the boundaries.

The interactive editor needs JavaScript. You can still read the prompt and copy the starter code below.

Starter code

export function spiralOrder(matrix: number[][]): number[] {
  // your solution here
  return []
}
solution.ts
Stuck? Hints available.

Haven't solved it yet?

Viewing the solution now may reduce learning. It's worth trying a bit more.

Open the reference solution

Without JavaScript, the reference solution is shown inline instead of in a dialog.

Solution

Final complexity

Time: O(m * n)

Space: O(1)

Solution 1 - Simulation with visited cells

Good baseline because it makes the traversal explicit.

export function spiralOrder(matrix: number[][]): number[] {
  const rows = matrix.length
  const cols = matrix[0].length
  const visited = Array.from({ length: rows }, () => new Array(cols).fill(false))
  const result: number[] = []
  const directions = [[0, 1], [1, 0], [0, -1], [-1, 0]]
  let directionIndex = 0
  let row = 0
  let col = 0

  for (let count = 0; count < rows * cols; count += 1) {
    result.push(matrix[row][col])
    visited[row][col] = true

    const nextRow = row + directions[directionIndex][0]
    const nextCol = col + directions[directionIndex][1]
    const outOfBounds = nextRow < 0 || nextRow >= rows || nextCol < 0 || nextCol >= cols

    if (outOfBounds || visited[nextRow][nextCol]) {
      directionIndex = (directionIndex + 1) % 4
    }

    row += directions[directionIndex][0]
    col += directions[directionIndex][1]
  }

  return result
}

It works, but there is a cleaner model for this kind of traversal.

Solution 2 - Boundaries

Now you walk the current layer and shrink the remaining rectangle.

export function spiralOrder(matrix: number[][]): number[] {
  const result: number[] = []
  let top = 0
  let bottom = matrix.length - 1
  let left = 0
  let right = matrix[0].length - 1

  while (top <= bottom && left <= right) {
    for (let col = left; col <= right; col += 1) {
      result.push(matrix[top][col])
    }
    top += 1

    for (let row = top; row <= bottom; row += 1) {
      result.push(matrix[row][right])
    }
    right -= 1

    if (top <= bottom) {
      for (let col = right; col >= left; col -= 1) {
        result.push(matrix[bottom][col])
      }
      bottom -= 1
    }

    if (left <= right) {
      for (let row = bottom; row >= top; row -= 1) {
        result.push(matrix[row][left])
      }
      left += 1
    }
  }

  return result
}

The strong insight here is using the boundaries as a representation of the remaining problem.

What to say in the interview

A strong short explanation would be:

The baseline uses current direction plus a visited matrix, but that carries too much state. The stronger version represents the current layer with four boundaries: top, bottom, left, and right. I walk the four borders that still exist and then shrink those boundaries, keeping only O(1) extra state.

That shows that you:

  • simplified the state
  • thought in layers instead of loose movement
  • handled the remaining-row and remaining-column edge cases

When this pattern shows up again

This pattern comes back when you need to:

  • traverse a matrix by layers
  • maintain clear boundaries
  • do simulation with minimal state

The point is not memorizing Spiral Matrix.

The point is learning to model what is still left to traverse.

Next challenge Serialize and Deserialize Binary Tree Previous challenge Longest Common Subsequence

Related challenges

Related articles