Talentd
FleetCode
Make and follow sheets.Sign in

Spiral Matrix solution and explanation

MediumArrayMatrixSimulation6 min read58.2% acceptanceUpdated 9 Apr 2026
Spiral Traversal of a Matrix | Spiral Matrixtake U forward · 16:33 · 522K viewsWatch on YouTube
9 more videos on this problem
  1. Spiral Traversal of a Matrix | Spiral Matrixtake U forward · 16:33 · 522K views
  2. Spiral Matrix - Microsoft Interview Question - Leetcode 54NeetCode · 16:46 · 239K views
  3. LeetCode Spiral Matrix Solution Explained - JavaNick White · 11:07 · 52K views
  4. Spiral Matrix (LeetCode 54) | Full Solution with Visualizations | Study AlgorithmsNikhil Lohia · 13:27 · 51K views
  5. Spiral Matrix - Leetcode 54 - Arrays & Strings (Python)Greg Hogg · 8:32 · 27K views
  6. Spiral Matrix - LeetCode 54 - JavaScriptAlgoJS · 8:39 · 4K views
  7. Spiral Matrix - LeetCode 54 - Python #blind75 #leetcode #spiralmatrixDeepti Talesra · 14:31 · 2K views
  8. LeetCode 54: Spiral Matrix - Interview Prep Ep 31Fisher Coder · 16:56 · 2K views
  9. LeetCode 54: Spiral Matrix | C# Solution | Matrix Traversal |Code Scribbler · 12:11 · 978 views
  10. Spiral Matrix | LeetCode 54 (Medium)Destination FAANG · 19:31 · 859 views

The problem

Given an m x n matrix, return all elements of the matrix in spiral order.

Example 1:

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

Example 2:

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

Approach overview

Problem Overview: Given an m × n matrix, return all elements in spiral order. The traversal starts at the top-left corner, moves right across the first row, then down the last column, then left across the bottom row, and continues inward layer by layer until every element is visited.

Approach 1: Iterative Boundary Management (O(m×n) time, O(1) space)

This approach simulates the spiral traversal by maintaining four boundaries: top, bottom, left, and right. You iterate across the top row, then move down the right column, then traverse the bottom row in reverse, and finally move up the left column. After completing each direction, adjust the corresponding boundary inward. The key insight is that every spiral layer shrinks the valid traversal region. Each matrix element is visited exactly once, giving O(m×n) time with constant extra space. This method fits naturally with problems involving array traversal and matrix boundary processing.

Approach 2: Recursive Divide and Conquer (O(m×n) time, O(m+n) recursion space)

The matrix can also be processed layer by layer using recursion. Each recursive call handles one outer ring of the matrix: traverse the top row, right column, bottom row (reverse), and left column (reverse). After processing the outer layer, the recursion continues with the inner submatrix defined by updated boundaries. The recursive structure mirrors the spiral pattern directly, which can make the logic easier to reason about. The tradeoff is additional recursion stack usage proportional to the number of layers, typically O(min(m,n)). This approach is a natural fit when practicing simulation or divide-and-conquer thinking.

Recommended for interviews: Iterative boundary management is the expected solution. Interviewers want to see careful boundary updates and correct handling of edge cases when rows or columns collapse. The recursive version demonstrates conceptual clarity but rarely improves performance. Showing the iterative solution proves you can control indices precisely and implement efficient matrix traversal.

Approach 1: Iterative Boundary Management

This approach involves simulating the spiral order by iteratively traversing matrix layers. The boundaries are adjusted after each complete traversal of a side (top, right, bottom, left). By shrinking the boundaries, we can eventually cover the entire matrix in a spiral order.

def spiralOrder(matrix):
    result = []
    if not matrix:
        return result
    top, bottom, left, right = 0, len(matrix) - 1, 0, len(matrix[0]) - 1
    while top <= bottom and left <= right:
        for i in range(left, right + 1):
            result.append(matrix[top][i])
        top += 1
        for i in range(top, bottom + 1):
            result.append(matrix[i][right])
        right -= 1
        if top <= bottom:
            for i in range(right, left - 1, -1):
                result.append(matrix[bottom][i])
            bottom -= 1
        if left <= right:
            for i in range(bottom, top - 1, -1):
                result.append(matrix[i][left])
            left += 1
    return result
import java.util.ArrayList;
import java.util.List;

public class SpiralMatrix {
    public List<Integer> spiralOrder(int[][] matrix) {
        List<Integer> result = new ArrayList<>();
        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;
    }
}
#include <vector>
using namespace std;

vector<int> spiralOrder(vector<vector<int>>& matrix) {
    vector<int> result;
    int top = 0, bottom = matrix.size() - 1, left = 0, right = 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;
}
var spiralOrder = function(matrix) {
    let result = [];
    let top = 0, bottom = matrix.length - 1;
    let left = 0, right = matrix[0].length - 1;
    while (top <= bottom && left <= right) {
        for (let i = left; i <= right; i++) result.push(matrix[top][i]);
        top++;
        for (let i = top; i <= bottom; i++) result.push(matrix[i][right]);
        right--;
        if (top <= bottom) {
            for (let i = right; i >= left; i--) result.push(matrix[bottom][i]);
            bottom--;
        }
        if (left <= right) {
            for (let i = bottom; i >= top; i--) result.push(matrix[i][left]);
            left++;
        }
    }
    return result;
};
#include <stdio.h>
#include <stdlib.h>

int* spiralOrder(int** matrix, int matrixSize, int* matrixColSize, int* returnSize) {
    int top = 0, bottom = matrixSize - 1, left = 0, right = matrixColSize[0] - 1;
    int* result = (int*)malloc((matrixSize * matrixColSize[0]) * sizeof(int));
    int k = 0;
    while (top <= bottom && left <= right) {
        for (int i = left; i <= right; ++i) result[k++] = matrix[top][i];
        top++;
        for (int i = top; i <= bottom; ++i) result[k++] = matrix[i][right];
        right--;
        if (top <= bottom) {
            for (int i = right; i >= left; --i) result[k++] = matrix[bottom][i];
            bottom--;
        }
        if (left <= right) {
            for (int i = bottom; i >= top; --i) result[k++] = matrix[i][left];
            left++;
        }
    }
    *returnSize = k;
    return result;
}
using System;
using System.Collections.Generic;

public class Solution {
    public IList<int> SpiralOrder(int[][] matrix) {
        var result = new List<int>();
        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;
    }
}

The Python solution uses lists and iterates over the given matrix using controlled loops defined by boundary variables. Each side's traversal is performed iteratively, ensuring the entire matrix is traversed in spiral form.

Complexity

Time Complexity: O(m * n) where m is the number of rows and n is the number of columns.

Space Complexity: O(1) additional space, excluding the result list.

Try this approach in the editor

Approach 2: Recursive Divide and Conquer

This approach uses recursion to divide the matrix into smaller sections. By processing the outer layer of the matrix, we can recursively call the function on the remaining submatrix. This continues until the entire matrix is covered.

def spiralOrder(matrix):
    result = []
    if matrix:
        def spiral_layer(top, bottom, left, right):
            if top > bottom or left > right:
                return
            result.extend(matrix[top][i] for i in range(left, right + 1))
            result.extend(matrix[i][right] for i in range(top + 1, bottom + 1))
            if top < bottom:
                result.extend(matrix[bottom][i] for i in range(right - 1, left - 1, -1))
            if left < right:
                result.extend(matrix[i][left] for i in range(bottom - 1, top, -1))
            spiral_layer(top + 1, bottom - 1, left + 1, right - 1)
        spiral_layer(0, len(matrix) - 1, 0, len(matrix[0]) - 1)
    return result

The Python solution defines a recursive helper function `spiral_layer`, with parameters defining the boundaries of the current submatrix. The base condition checks if the boundary indices are valid. It extracts and appends elements to the result, then recursively narrows the boundaries until they converge.

Complexity

Time Complexity: O(m * n) since each recursive call processes an entire layer of the matrix.

Space Complexity: O(m * n) due to the recursive call stack usage, although tail recursion may optimize this depending on the Python implementation.

Try this approach in the editor

Approach 3: Simulation

We can simulate the entire traversal process. We use i and j to represent the row and column of the current element being visited, and k to represent the current direction. We use an array or hash table vis to record whether each element has been visited. Each time we visit an element, we mark it as visited, then move one step forward in the current direction. If moving forward results in an out-of-bounds condition or the element has already been visited, we change direction and continue moving forward until the entire matrix has been traversed.

The time complexity is O(m times n), and the space complexity is O(m times n). Here, m and n are the number of rows and columns of the matrix, respectively.

function spiralOrder(matrix: number[][]): number[] {
    const m = matrix.length;
    const n = matrix[0].length;
    const ans: number[] = [];
    const vis: boolean[][] = Array.from({ length: m }, () => Array(n).fill(false));
    const dirs = [0, 1, 0, -1, 0];
    for (let h = m * n, i = 0, j = 0, k = 0; h > 0; --h) {
        ans.push(matrix[i][j]);
        vis[i][j] = true;
        const x = i + dirs[k];
        const y = j + dirs[k + 1];
        if (x < 0 || x >= m || y < 0 || y >= n || vis[x][y]) {
            k = (k + 1) % 4;
        }
        i += dirs[k];
        j += dirs[k + 1];
    }
    return ans;
}
func spiralOrder(matrix [][]int) (ans []int) {
	m, n := len(matrix), len(matrix[0])
	vis := make([][]bool, m)
	for i := range vis {
		vis[i] = make([]bool, n)
	}
	dirs := [5]int{0, 1, 0, -1, 0}
	i, j, k := 0, 0, 0
	for h := m * n; h > 0; h-- {
		ans = append(ans, matrix[i][j])
		vis[i][j] = true
		x, y := i+dirs[k], j+dirs[k+1]
		if x < 0 || x >= m || y < 0 || y >= n || vis[x][y] {
			k = (k + 1) % 4
		}
		i, j = i+dirs[k], j+dirs[k+1]
	}
	return
}
impl Solution {
    pub fn spiral_order(matrix: Vec<Vec<i32>>) -> Vec<i32> {
        let m = matrix.len();
        let n = matrix[0].len();
        let mut dirs = vec![0, 1, 0, -1, 0];
        let mut vis = vec![vec![false; n]; m];
        let mut i = 0;
        let mut j = 0;
        let mut k = 0;
        let mut ans = Vec::new();

        for _ in 0..(m * n) {
            ans.push(matrix[i][j]);
            vis[i][j] = true;
            let x = i as i32 + dirs[k] as i32;
            let y = j as i32 + dirs[k + 1] as i32;

            if x < 0 || x >= m as i32 || y < 0 || y >= n as i32 || vis[x as usize][y as usize] {
                k = (k + 1) % 4;
            }

            i = (i as i32 + dirs[k] as i32) as usize;
            j = (j as i32 + dirs[k + 1] as i32) as usize;
        }

        ans
    }
}
Try this approach in the editor

Approach 4: Simulation (Space Optimization)

Notice that the range of matrix element values is [-100, 100]. Therefore, we can add a large value, such as 300, to the visited elements. This way, we only need to check if the visited element is greater than 100, without needing extra space to record whether it has been visited. If we need to restore the original values of the visited elements, we can traverse the matrix again after the traversal is complete and subtract 300 from all elements.

The time complexity is O(m times n), where m and n are the number of rows and columns of the matrix, respectively. The space complexity is O(1).

function spiralOrder(matrix: number[][]): number[] {
    const m = matrix.length;
    const n = matrix[0].length;
    const ans: number[] = [];
    const dirs = [0, 1, 0, -1, 0];
    for (let h = m * n, i = 0, j = 0, k = 0; h > 0; --h) {
        ans.push(matrix[i][j]);
        matrix[i][j] += 300;
        const x = i + dirs[k];
        const y = j + dirs[k + 1];
        if (x < 0 || x >= m || y < 0 || y >= n || matrix[x][y] > 100) {
            k = (k + 1) % 4;
        }
        i += dirs[k];
        j += dirs[k + 1];
    }
    for (let i = 0; i < m; ++i) {
        for (let j = 0; j < n; ++j) {
            matrix[i][j] -= 300;
        }
    }
    return ans;
}
func spiralOrder(matrix [][]int) (ans []int) {
	m, n := len(matrix), len(matrix[0])
	dirs := [5]int{0, 1, 0, -1, 0}
	i, j, k := 0, 0, 0
	for h := m * n; h > 0; h-- {
		ans = append(ans, matrix[i][j])
		matrix[i][j] += 300
		x, y := i+dirs[k], j+dirs[k+1]
		if x < 0 || x >= m || y < 0 || y >= n || matrix[x][y] > 100 {
			k = (k + 1) % 4
		}
		i, j = i+dirs[k], j+dirs[k+1]
	}
	for i = 0; i < m; i++ {
		for j = 0; j < n; j++ {
			matrix[i][j] -= 300
		}
	}
	return
}
impl Solution {
    pub fn spiral_order(mut matrix: Vec<Vec<i32>>) -> Vec<i32> {
        let m = matrix.len();
        let n = matrix[0].len();
        let mut dirs = vec![0, 1, 0, -1, 0];
        let mut i = 0;
        let mut j = 0;
        let mut k = 0;
        let mut ans = Vec::new();

        for _ in 0..(m * n) {
            ans.push(matrix[i][j]);
            matrix[i][j] += 300;
            let x = i as i32 + dirs[k] as i32;
            let y = j as i32 + dirs[k + 1] as i32;

            if x < 0
                || x >= m as i32
                || y < 0
                || y >= n as i32
                || matrix[x as usize][y as usize] > 100
            {
                k = (k + 1) % 4;
            }

            i = (i as i32 + dirs[k] as i32) as usize;
            j = (j as i32 + dirs[k + 1] as i32) as usize;
        }

        for i in 0..m {
            for j in 0..n {
                matrix[i][j] -= 300;
            }
        }

        ans
    }
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Iterative Boundary Management

Time Complexity: O(m * n) where m is the number of rows and n is the number of columns.

Space Complexity: O(1) additional space, excluding the result list.

Recursive Divide and Conquer

Time Complexity: O(m * n) since each recursive call processes an entire layer of the matrix.

Space Complexity: O(m * n) due to the recursive call stack usage, although tail recursion may optimize this depending on the Python implementation.

Simulation–
Simulation (Space Optimization)–

Complexity analysis

ApproachTimeSpaceWhen to Use
Iterative Boundary ManagementO(m×n)O(1)Best general solution. Efficient traversal with constant extra memory.
Recursive Divide and ConquerO(m×n)O(m+n) recursion stackUseful for conceptual clarity or recursion practice when processing matrix layers.

Frequently asked questions

Is Spiral Matrix easy or hard?

Spiral Matrix is considered a medium difficulty problem. The core idea is straightforward, but correctly updating boundaries and avoiding duplicate traversal when rows or columns collapse requires careful implementation.

How to solve Spiral Matrix in O(n)?

For an m×n matrix, the optimal complexity is O(m×n) because every element must be visited to produce the output order. The typical solution uses boundary pointers to simulate spiral movement across rows and columns. This ensures each element is appended once while maintaining constant extra space.

What is the best approach for Spiral Matrix?

The best approach uses iterative boundary management. Maintain four pointers (top, bottom, left, right) and traverse the matrix edges while shrinking the boundaries after each direction. Every element is visited exactly once, giving O(m×n) time and O(1) extra space. This is the solution most interviewers expect.

What data structure is used in Spiral Matrix?

The problem primarily uses a 2D array (matrix). The algorithm relies on index boundaries rather than additional data structures, which keeps space complexity at O(1). The result is stored in a list or array while traversing the matrix in spiral order.

What is the time complexity of Spiral Matrix?

The time complexity is O(m×n) where m is the number of rows and n is the number of columns. Each matrix element is processed exactly once during the spiral traversal. No repeated scans occur, so the runtime grows linearly with the number of elements.

Spiral Matrix Python or Java solution approach?

Both Python and Java implementations follow the same logic: maintain four boundaries and iterate across rows or columns depending on the current direction. After each traversal, update the boundary pointer. The algorithm remains O(m×n) time and O(1) extra space regardless of language.

Is Spiral Matrix asked at Google, Amazon, or Meta?

Spiral Matrix appears frequently in coding interviews at large tech companies including Amazon, Google, and Meta. The problem tests matrix traversal, careful index management, and edge‑case handling when rows or columns shrink during iteration.

Comments

Now write it yourselfClose this page and solve Spiral Matrix in the editor, without looking.
Practise Spiral Matrix