Placement Prep

Spiral Order Matrix Printing: Step-by-Step Guide with Code

Print a 2D matrix in spiral order using four boundary pointers. Hand-traced 3x4 example, edge-case guards, and verified code in Python, C++, Java, and C.

By FACE Prep Team 6 min read
matrix spiral-order python java arrays placement-prep data-structures

Printing a 2D matrix in spiral order is a standard interview problem solved with four boundary pointers: top, bottom, left, and right.

The approach visits every element exactly once and runs in O(M x N) time. LeetCode Problem 54 (Spiral Matrix) is the canonical version of this problem; the same four-pointer logic appears in CRT coding rounds at mass-recruiting IT firms.

The Traversal Pattern

A spiral sweep moves through a 2D matrix in four directions, cycling through them until all elements are covered:

  1. Left to right across the top row.
  2. Top to bottom down the rightmost column.
  3. Right to left across the bottom row.
  4. Bottom to top up the leftmost column.

After each full cycle, the active region shrinks by one layer inward. The boundary pointers track which rows and columns still contain unvisited elements.

The Four-Pointer Approach

Setup

Initialize four variables:

  • top = 0, bottom = rows - 1
  • left = 0, right = cols - 1

The while loop continues as long as top <= bottom and left <= right. Both conditions must hold; once either fails, all elements have been visited.

The Four Inner Passes

Each iteration of the outer loop runs four inner passes in this fixed order:

  • Top row pass: traverse column indices from left to right in row top. Then increment top.
  • Right column pass: traverse row indices from top to bottom in column right. Then decrement right.
  • Bottom row pass (guarded): only if top <= bottom, traverse column indices from right down to left in row bottom. Then decrement bottom.
  • Left column pass (guarded): only if left <= right, traverse row indices from bottom down to top in column left. Then increment left.

The guards on the last two passes prevent double-printing on single-row and single-column matrices. The edge cases section below has the worked demonstration.

Hand-Tracing a 3x4 Example

Input matrix:

 1  2  3  4
 5  6  7  8
 9 10 11 12

Initial state: top=0, bottom=2, left=0, right=3.

Iteration 1:

  • Top row pass: columns 0 to 3 of row 0 give 1, 2, 3, 4. top becomes 1.
  • Right column pass: rows 1 to 2 of column 3 give 8, 12. right becomes 2.
  • Bottom row pass (top ≤ bottom: 1 ≤ 2 ✓): columns 2 to 0 of row 2 give 11, 10, 9. bottom becomes 1.
  • Left column pass (left ≤ right: 0 ≤ 2 ✓): rows 1 to 1 of column 0 give 5. left becomes 1.

State after iteration 1: top=1, bottom=1, left=1, right=2. Collected so far: 1, 2, 3, 4, 8, 12, 11, 10, 9, 5.

Iteration 2:

  • Top row pass: columns 1 to 2 of row 1 give 6, 7. top becomes 2.
  • Right column pass: range(2, 2) is empty. right becomes 1.
  • Bottom row pass: top(2) ≤ bottom(1)? No. Skip.
  • Left column pass: left(1) ≤ right(1) ✓. range(1, 1, -1) is empty. left becomes 2.

Loop condition: top(2) ≤ bottom(1)? No. Exit.

Final output: 1, 2, 3, 4, 8, 12, 11, 10, 9, 5, 6, 7 (12 elements total ✓).

The Edge Cases That Cause Double-Printing

Without the two guards, the algorithm produces incorrect results for two specific matrix shapes.

Single-row matrix

The input [[1, 2, 3]] has top=0, bottom=0, left=0, right=2.

  • Top row pass: 1, 2, 3. top becomes 1.
  • Right column pass: range(1, 1) is empty. right becomes 1.
  • Without the guard: the bottom row pass runs from column 1 down to column 0 in row 0, printing 2 and 1 again. Incorrect output.
  • With if top <= bottom: 1 ≤ 0 evaluates to false. Bottom row pass is skipped. Correct.

Single-column matrix

The input [[1], [2], [3]] has top=0, bottom=2, left=0, right=0.

  • Top row pass: row 0, column 0 gives 1. top becomes 1.
  • Right column pass: rows 1 to 2 of column 0 give 2, 3. right becomes -1.
  • Bottom row pass: top(1) ≤ bottom(2) ✓. Column index range(-1, -1, -1) is empty. bottom becomes 1.
  • Without the guard: the left column pass runs from row 1 to row 2 in column 0, printing 2 again. Incorrect output.
  • With if left <= right: 0 ≤ -1 evaluates to false. Left column pass is skipped. Correct.

These two guards appear in every correct reference implementation and should be part of any code you write under time pressure.

Working Code in Four Languages

The Python programs collection on FACE Prep covers the foundational building blocks that appear alongside this problem in CRT rounds; the code below extends those patterns to two dimensions.

Python

def spiral_order(matrix):
    if not matrix or not matrix[0]:
        return []
    top, bottom = 0, len(matrix) - 1
    left, right = 0, len(matrix[0]) - 1
    result = []
    while top <= bottom and left <= right:
        # Top row: left to right
        for i in range(left, right + 1):
            result.append(matrix[top][i])
        top += 1
        # Right column: top to bottom
        for i in range(top, bottom + 1):
            result.append(matrix[i][right])
        right -= 1
        # Bottom row: right to left
        # Guard prevents double-print when only one row remains
        if top <= bottom:
            for i in range(right, left - 1, -1):
                result.append(matrix[bottom][i])
            bottom -= 1
        # Left column: bottom to top
        # Guard prevents double-print when only one column remains
        if left <= right:
            for i in range(bottom, top - 1, -1):
                result.append(matrix[i][left])
            left += 1
    return result

# Test with the 3x4 example
matrix = [
    [1,  2,  3,  4],
    [5,  6,  7,  8],
    [9, 10, 11, 12]
]
print(spiral_order(matrix))
# Output: [1, 2, 3, 4, 8, 12, 11, 10, 9, 5, 6, 7]

The Python documentation for range() specifies that range(start, stop, step) with a negative step generates a decreasing sequence from start down to stop + 1, inclusive. The reverse sweeps depend on this: range(right, left - 1, -1) covers column indices from right down to left.

C++

#include <iostream>
#include <vector>
using namespace std;

vector<int> spiralOrder(vector<vector<int>>& matrix) {
    vector<int> result;
    if (matrix.empty() || matrix[0].empty()) return result;
    int top = 0, bottom = (int)matrix.size() - 1;
    int left = 0, right = (int)matrix[0].size() - 1;
    while (top <= bottom && left <= right) {
        for (int i = left; i <= right; i++)
            result.push_back(matrix[top][i]);
        top++;
        for (int i = top; i <= bottom; i++)
            result.push_back(matrix[i][right]);
        right--;
        if (top <= bottom) {
            for (int i = right; i >= left; i--)
                result.push_back(matrix[bottom][i]);
            bottom--;
        }
        if (left <= right) {
            for (int i = bottom; i >= top; i--)
                result.push_back(matrix[i][left]);
            left++;
        }
    }
    return result;
}

Java

import java.util.ArrayList;
import java.util.List;

public class SpiralMatrix {
    public static List<Integer> spiralOrder(int[][] matrix) {
        List<Integer> result = new ArrayList<>();
        if (matrix == null || matrix.length == 0) return result;
        int top = 0, bottom = matrix.length - 1;
        int left = 0, right = matrix[0].length - 1;
        while (top <= bottom && left <= right) {
            for (int i = left; i <= right; i++)
                result.add(matrix[top][i]);
            top++;
            for (int i = top; i <= bottom; i++)
                result.add(matrix[i][right]);
            right--;
            if (top <= bottom) {
                for (int i = right; i >= left; i--)
                    result.add(matrix[bottom][i]);
                bottom--;
            }
            if (left <= right) {
                for (int i = bottom; i >= top; i--)
                    result.add(matrix[i][left]);
                left++;
            }
        }
        return result;
    }
}

C

#include <stdio.h>

/* cols is the compile-time column count; change to match your matrix */
void spiralPrint(int matrix[][4], int rows, int cols) {
    int top = 0, bottom = rows - 1;
    int left = 0, right = cols - 1;
    int i;
    while (top <= bottom && left <= right) {
        for (i = left; i <= right; i++)
            printf("%d ", matrix[top][i]);
        top++;
        for (i = top; i <= bottom; i++)
            printf("%d ", matrix[i][right]);
        right--;
        if (top <= bottom) {
            for (i = right; i >= left; i--)
                printf("%d ", matrix[bottom][i]);
            bottom--;
        }
        if (left <= right) {
            for (i = bottom; i >= top; i--)
                printf("%d ", matrix[i][left]);
            left++;
        }
    }
}

int main(void) {
    int m[3][4] = {
        { 1,  2,  3,  4},
        { 5,  6,  7,  8},
        { 9, 10, 11, 12}
    };
    spiralPrint(m, 3, 4);
    /* Output: 1 2 3 4 8 12 11 10 9 5 6 7 */
    return 0;
}

Time and Space Complexity

  • Time: O(M x N). Every element in the matrix is visited exactly once across all inner loop passes. The outer while loop runs at most min(M, N) / 2 iterations, and cumulative element visits across all iterations sum to M x N.
  • Auxiliary space: O(1). The four boundary variables and loop counter i are the only extra allocations. The output list is O(M x N) in size, but that is the required output, not workspace.

For placement coding rounds, O(M x N) time and O(1) auxiliary space is the expected answer. Questions that ask for in-place spiral rotation (no output list) require a different approach, but spiral printing specifically always collects into a result container.

The array traversal patterns in the sum of array elements article cover the same O(N) accumulator logic for single-dimension arrays. The spiral extension adds a second dimension and the boundary-shrinking mechanism, but the underlying loop structure is the same.

The range() step-function logic in the reverse sweeps here is also at the core of Python’s iterator and generator system. TinkerLLM’s build exercises at ₹299 work through these patterns in the context of real data-processing tasks, which is more durable than solving placement problems in isolation.

Primary sources

Frequently asked questions

What is the time complexity of spiral matrix printing?

O(M x N), where M is the number of rows and N is the number of columns. Every element is visited exactly once across the four inner loops, regardless of matrix shape.

Why do we need the if top <= bottom guard in the spiral algorithm?

Without this guard, a single-row matrix double-prints its elements during the bottom-row reverse pass. After the top-row sweep increments top past bottom, the reverse loop must be skipped.

Does the algorithm work correctly for a 1x1 matrix?

Yes. A 1x1 matrix has top=bottom=0 and left=right=0. The top-row sweep prints the single element, top becomes 1, and both subsequent guards evaluate to false. Nothing else is printed.

How is spiral matrix traversal different from a simple row-by-row traversal?

Row-by-row traversal moves in one direction without shrinking boundaries. Spiral traversal moves in four directions, shrinking the active region after each sweep until all elements are covered.

Where does this problem appear in placement tests?

Spiral order matrix printing is a standard array-traversal problem in placement coding rounds. It also appears as LeetCode Problem 54 and in CRT coding assessments at Tier-2 and Tier-3 engineering colleges.

What is the auxiliary space complexity of the four-pointer approach?

O(1) auxiliary space. Only four integer variables (top, bottom, left, right) and loop counters are needed. The result array is O(M x N) but is the function output, not extra workspace.

Build AI projects

A self-paced playground for building with LLMs.

TinkerLLM is FACE Prep's sister property. A guided environment for shipping real LLM applications, the kind of project that earns a paragraph on your resume, not a line.

Try TinkerLLM (₹299 launch)
Free AI Roadmap PDF