DEV Community

Cover image for Spiral Matrix Traversal, Explained Step by Step (with a visualizer you can scrub)
Salman Sadik Siddiquee
Salman Sadik Siddiquee

Posted on AI-assisted

Spiral Matrix Traversal, Explained Step by Step (with a visualizer you can scrub)

Spiral traversal is the grid question that looks easy on the whiteboard and then quietly breaks on a 1 × 4 matrix. I kept getting it wrong while learning, so I built a visualizer where you can step through it one cell at a time and watch the four bounds close in. This post is the explanation I wish I had, plus the exact code the visualizer runs.

Try it out: Spiral Traversal visualizer (free, no sign-up, works on a phone).

What spiral traversal is

Take a 2D array and read it the way you would peel an onion: along the top row, down the right column, back along the bottom row, up the left column, then the same again on the smaller rectangle inside, until nothing is left.

On this 3 × 4 grid:

 1  2  3  4
 5  6  7  8
 9 10 11 12
Enter fullscreen mode Exit fullscreen mode

the spiral order is

1 2 3 4 8 12 11 10 9 5 6 7
Enter fullscreen mode Exit fullscreen mode

Ring one is the outer boundary (1 → 4 → 12 → 9 → 5). Ring two is what remains in the middle: 6 and 7. Every cell is visited exactly once, so the whole thing costs one step per cell, rows × cols.

The idea: four bounds that close in

Don't think in rings. Think in four numbers that describe the rectangle still to be visited:

  • top and bottom: the first and last row that still have unvisited cells
  • left and right: the first and last such column

Walk one edge, then move that edge's bound inwards by one. Top row done? top += 1. Right column done? right -= 1. And so on. When top passes bottom or left passes right, the rectangle is empty and you stop.

Here are the bounds on the 3 × 4 grid, ring by ring:

After walking Visited top bottom left right
(start) 0 2 0 3
top row 1 2 3 4 1 2 0 3
right column 8 12 1 2 0 2
bottom row (backwards) 11 10 9 1 1 0 2
left column (upwards) 5 1 1 1 2
top row again 6 7 2 1 1 2

After that last step top (2) is greater than bottom (1), so the loop ends. Twelve cells, twelve visits.

The code

The visualizer runs exactly this. visit is whatever you want to do with a cell: print it, push it to a list, add it to a sum.

Python

top, bottom, left, right = 0, rows - 1, 0, cols - 1
while top <= bottom and left <= right:
    for j in range(left, right + 1):
        visit(a[top][j])
    top += 1
    for i in range(top, bottom + 1):
        visit(a[i][right])
    right -= 1
    if top <= bottom:
        for j in range(right, left - 1, -1):
            visit(a[bottom][j])
        bottom -= 1
    if left <= right:
        for i in range(bottom, top - 1, -1):
            visit(a[i][left])
        left += 1
Enter fullscreen mode Exit fullscreen mode

JavaScript

function spiral(a, visit) {
    const rows = a.length, cols = a[0].length;
    let top = 0, bottom = rows - 1, left = 0, right = cols - 1;
    while (top <= bottom && left <= right) {
        for (let j = left; j <= right; j++) visit(a[top][j]);
        top++;
        for (let i = top; i <= bottom; i++) visit(a[i][right]);
        right--;
        if (top <= bottom) {
            for (let j = right; j >= left; j--) visit(a[bottom][j]);
            bottom--;
        }
        if (left <= right) {
            for (let i = bottom; i >= top; i--) visit(a[i][left]);
            left++;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

C++

int top = 0, bottom = rows - 1, left = 0, right = cols - 1;
while (top <= bottom && left <= right) {
    for (int j = left; j <= right; j++) visit(a[top][j]);
    top++;
    for (int i = top; i <= bottom; i++) visit(a[i][right]);
    right--;
    if (top <= bottom) {
        for (int j = right; j >= left; j--) visit(a[bottom][j]);
        bottom--;
    }
    if (left <= right) {
        for (int i = bottom; i >= top; i--) visit(a[i][left]);
        left++;
    }
}
Enter fullscreen mode Exit fullscreen mode

Notice the order of operations on each edge: walk the edge first, then move the bound. Moving top before walking the top row skips a whole row.

The two if checks are not optional

This is the part that breaks. Remove the two guards and run it on a single row:

1 2 3 4
Enter fullscreen mode Exit fullscreen mode

Without the guards, you get 1 2 3 4 3 2 1. After the top row, top becomes 1 and is already past bottom (0). The right-column loop runs zero times, fine. But the bottom-row loop happily walks row bottom, which is the row you just printed, backwards. The guard if top <= bottom is what says "there is no separate bottom row left, skip it".

The same thing happens with a single column and the left-column walk, which is what if left <= right prevents.

It also bites on ordinary grids on the last ring. On the 3 × 4 example, without the guards the output ends … 5 6 7 6: the final single-row ring gets its 7 → 6 walked back. A square grid hides the bug, which is exactly why people ship it: on a 3 × 3 the unguarded version happens to print 1 2 3 6 9 8 7 4 5, which is correct.

Set the rows slider to 1 in the visualizer and step through it. You will see the guard skip the bottom edge.

Edge cases to test

  • 1 × n and n × 1: the guards handle them. A 1 × 4 gives 1 2 3 4, a 4 × 1 also gives 1 2 3 4.
  • 1 × 1: one visit, then top passes bottom.
  • Empty grid: check rows == 0 before reading a[0].length.
  • Non-square grids: always test on 3 × 4 and 4 × 3. Square grids hide off-by-one bugs.

Complexity

Time O(rows × cols): every cell is visited once, and the bookkeeping is a constant amount per ring. Extra memory O(1) if you print or stream; O(rows × cols) if you collect the output into a list, which is the output itself, not overhead.

You do not need a visited boolean matrix. It works, but it costs a second grid of memory and one more thing to get wrong.

Variants you will meet

  • LeetCode 54, Spiral Matrix: this exact function, returning the list.
  • LeetCode 59, Spiral Matrix II: the reverse, fill an empty n × n grid with 1 … n² in spiral order. Same loop, write instead of read.
  • Anticlockwise: walk the left column down first, then the bottom row, then the right column up, then the top row back.
  • Spiral from the centre: run the clockwise spiral, collect the cells, reverse the list.
  • Boundary only: the first ring is a full traversal in its own right. If you can write the boundary correctly, the spiral is a loop around it. There is a boundary traversal page too.

Try it with your own grid

Two things I built specifically for learning this:

  1. Every step of the visualizer is linkable. Scrub to the moment top passes bottom and press Copy link to this step; the URL reopens exactly that state. Teachers: send one link to a class.
  2. Paste any matrix into the 2D Array Visualizer (JSON, a Python list, or plain rows of numbers), then click Traverse this grid → Spiral. Your grid travels with you, and you can switch to row-major, snake, or diagonal order without losing it.

Everything is open source (MIT): github.com/salsadsid/visualizer. Each traversal is a small function that records steps; the visualizer plays them back. If a traversal or a grid algorithm is missing that you would like to see, say so in the comments or open an issue.

Which one should I visualize next: flood fill, number of islands, or BFS in a maze?

Top comments (1)

Collapse
 
tas25454545 profile image
Tas •

Solid visualization. Keep more coming..👋