Spiral Matrix
Given a 2D matrix, return a list containing all of its elements in spiral order.
Note: Spiral order means traversing the matrix in a clockwise spiral, starting from the top-left corner and moving right, then down, then left, then up, and repeating.
Example(s)
Example 1:
Input: matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
Output: [1, 2, 3, 6, 9, 8, 7, 4, 5]
Explanation:
Start at top-left (1), go right: 1→2→3
Then go down: 6→9
Then go left: 8→7
Then go up: 4
Then go right: 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]
Explanation:
Right: 1→2→3→4
Down: 8→12
Left: 11→10→9
Up: 5
Right: 6→7
Example 3:
Input: matrix = [
[1, 2],
[3, 4]
]
Output: [1, 2, 4, 3]
Example 4:
Input: matrix = [[1]]
Output: [1]
Solution
The solution uses boundary tracking:
- Define boundaries: Track top, bottom, left, right boundaries
- Traverse in layers: Process the outer layer, then move inward
- Four directions:
- Right: from left to right along top row
- Down: from top to bottom along right column
- Left: from right to left along bottom row
- Up: from bottom to top along left column
- Update boundaries: After each direction, update the boundary
- Terminate: Stop when boundaries cross
- JavaScript Solution
- Python Solution
JavaScript Solution - Boundary Tracking
/**
* Traverse matrix in spiral order
* @param {number[][]} matrix - 2D matrix
* @return {number[]} - Elements in spiral order
*/
function spiralOrder(matrix) {
if (!matrix || matrix.length === 0 || matrix[0].length === 0) {
return [];
}
const result = [];
let top = 0;
let bottom = matrix.length - 1;
let left = 0;
let right = matrix[0].length - 1;
while (top <= bottom && left <= right) {
// Traverse right along top row
for (let i = left; i <= right; i++) {
result.push(matrix[top][i]);
}
top++; // Move top boundary down
// Traverse down along right column
for (let i = top; i <= bottom; i++) {
result.push(matrix[i][right]);
}
right--; // Move right boundary left
// Traverse left along bottom row (if still valid)
if (top <= bottom) {
for (let i = right; i >= left; i--) {
result.push(matrix[bottom][i]);
}
bottom--; // Move bottom boundary up
}
// Traverse up along left column (if still valid)
if (left <= right) {
for (let i = bottom; i >= top; i--) {
result.push(matrix[i][left]);
}
left++; // Move left boundary right
}
}
return result;
}
// Test cases
const matrix1 = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
console.log('Example 1:', spiralOrder(matrix1));
// [1, 2, 3, 6, 9, 8, 7, 4, 5]
const matrix2 = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
];
console.log('Example 2:', spiralOrder(matrix2));
// [1, 2, 3, 4, 8, 12, 11, 10, 9, 5, 6, 7]
const matrix3 = [
[1, 2],
[3, 4]
];
console.log('Example 3:', spiralOrder(matrix3));
// [1, 2, 4, 3]
const matrix4 = [[1]];
console.log('Example 4:', spiralOrder(matrix4));
// [1]Output:
Click "Run Code" to execute the code and see the results.
Python Solution - Boundary Tracking
from typing import List
def spiral_order(matrix: List[List[int]]) -> List[int]:
"""
Traverse matrix in spiral order
Args:
matrix: 2D matrix
Returns:
List[int]: Elements in spiral order
"""
if not matrix or not matrix[0]:
return []
result = []
top = 0
bottom = len(matrix) - 1
left = 0
right = len(matrix[0]) - 1
while top <= bottom and left <= right:
# Traverse right along top row
for i in range(left, right + 1):
result.append(matrix[top][i])
top += 1 # Move top boundary down
# Traverse down along right column
for i in range(top, bottom + 1):
result.append(matrix[i][right])
right -= 1 # Move right boundary left
# Traverse left along bottom row (if still valid)
if top <= bottom:
for i in range(right, left - 1, -1):
result.append(matrix[bottom][i])
bottom -= 1 # Move bottom boundary up
# Traverse up along left column (if still valid)
if left <= right:
for i in range(bottom, top - 1, -1):
result.append(matrix[i][left])
left += 1 # Move left boundary right
return result
# Test cases
matrix1 = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
print('Example 1:', spiral_order(matrix1))
# [1, 2, 3, 6, 9, 8, 7, 4, 5]
matrix2 = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
]
print('Example 2:', spiral_order(matrix2))
# [1, 2, 3, 4, 8, 12, 11, 10, 9, 5, 6, 7]
matrix3 = [
[1, 2],
[3, 4]
]
print('Example 3:', spiral_order(matrix3))
# [1, 2, 4, 3]
matrix4 = [[1]]
print('Example 4:', spiral_order(matrix4))
# [1]Loading Python runtime...
Output:
Click "Run Code" to execute the code and see the results.
Alternative Solution (Direction-Based)
Here's a direction-based approach using a direction vector:
- JavaScript Direction-Based
- Python Direction-Based
/**
* Direction-based approach
*/
function spiralOrderDirection(matrix) {
if (!matrix || matrix.length === 0) return [];
const result = [];
const rows = matrix.length;
const cols = matrix[0].length;
const visited = Array(rows).fill().map(() => Array(cols).fill(false));
// Directions: right, down, left, up
const directions = [[0, 1], [1, 0], [0, -1], [-1, 0]];
let dir = 0; // Start with right
let row = 0, col = 0;
for (let i = 0; i < rows * cols; i++) {
result.push(matrix[row][col]);
visited[row][col] = true;
// Calculate next position
const nextRow = row + directions[dir][0];
const nextCol = col + directions[dir][1];
// Check if we need to change direction
if (
nextRow < 0 || nextRow >= rows ||
nextCol < 0 || nextCol >= cols ||
visited[nextRow][nextCol]
) {
// Change direction (right -> down -> left -> up -> right)
dir = (dir + 1) % 4;
}
// Move to next position
row += directions[dir][0];
col += directions[dir][1];
}
return result;
}
def spiral_order_direction(matrix: List[List[int]]) -> List[int]:
"""
Direction-based approach
"""
if not matrix or not matrix[0]:
return []
rows = len(matrix)
cols = len(matrix[0])
visited = [[False] * cols for _ in range(rows)]
result = []
# Directions: right, down, left, up
directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]
dir_idx = 0 # Start with right
row, col = 0, 0
for _ in range(rows * cols):
result.append(matrix[row][col])
visited[row][col] = True
# Calculate next position
next_row = row + directions[dir_idx][0]
next_col = col + directions[dir_idx][1]
# Check if we need to change direction
if (
next_row < 0 or next_row >= rows or
next_col < 0 or next_col >= cols or
visited[next_row][next_col]
):
# Change direction
dir_idx = (dir_idx + 1) % 4
# Move to next position
row += directions[dir_idx][0]
col += directions[dir_idx][1]
return result
Complexity
- Time Complexity: O(m × n) - Where m is the number of rows and n is the number of columns. We visit each cell exactly once.
- Space Complexity: O(1) - For the boundary tracking approach, we only use a constant amount of extra space (excluding the output array). For the direction-based approach, it's O(m × n) for the visited array.
Approach
The solution uses boundary tracking:
-
Initialize boundaries:
top = 0,bottom = rows - 1left = 0,right = cols - 1
-
Traverse in four directions:
- Right: From
lefttorightalongtoprow, thentop++ - Down: From
toptobottomalongrightcolumn, thenright-- - Left: From
righttoleftalongbottomrow, thenbottom-- - Up: From
bottomtotopalongleftcolumn, thenleft++
- Right: From
-
Boundary checks: Before traversing left and up, check if boundaries are still valid
-
Termination: Stop when
top > bottomorleft > right
Key Insights
- Boundary tracking: Maintain four boundaries that shrink as we traverse
- Layer by layer: Process outer layer, then move inward
- Four directions: Always traverse in the same order (right, down, left, up)
- Boundary validation: Check boundaries before left and up to avoid duplicates
- Single pass: Each element is visited exactly once
Step-by-Step Example
Let's trace through Example 1: matrix = [[1,2,3],[4,5,6],[7,8,9]]
Initial: top=0, bottom=2, left=0, right=2
Iteration 1:
Right: left=0 to right=2
result = [1, 2, 3]
top = 1
Down: top=1 to bottom=2
result = [1, 2, 3, 6, 9]
right = 1
Left: right=1 to left=0 (top=1 <= bottom=2 ✓)
result = [1, 2, 3, 6, 9, 8, 7]
bottom = 1
Up: bottom=1 to top=1 (left=0 <= right=1 ✓)
result = [1, 2, 3, 6, 9, 8, 7, 4]
left = 1
Iteration 2:
Right: left=1 to right=1
result = [1, 2, 3, 6, 9, 8, 7, 4, 5]
top = 2
Check: top=2 > bottom=1 → break
Final: [1, 2, 3, 6, 9, 8, 7, 4, 5]
Visual Representation
Matrix: Spiral traversal:
[1, 2, 3] → → →
[4, 5, 6] → ↓
[7, 8, 9] ← ← ← ↑
Step 1: Right → 1, 2, 3
Step 2: Down → 6, 9
Step 3: Left → 8, 7
Step 4: Up → 4
Step 5: Right → 5
Edge Cases
- Empty matrix: Return
[] - Single row:
[[1, 2, 3]]→[1, 2, 3] - Single column:
[[1], [2], [3]]→[1, 2, 3] - Single element:
[[1]]→[1] - Rectangular matrix: Works for any m × n matrix
- 1×n or n×1: Handled correctly
Important Notes
- Boundary checks: The checks
if (top <= bottom)andif (left <= right)are crucial to avoid processing the same row/column twice - Order matters: Always traverse in the order: right → down → left → up
- Boundary updates: Update boundaries after each direction
- Rectangular matrices: Works for both square and rectangular matrices
Related Problems
- Spiral Matrix II: Generate a matrix in spiral order
- Spiral Matrix III: Spiral traversal starting from a specific position
- Rotate Image: Rotate matrix 90 degrees (related concept)
- Set Matrix Zeroes: Matrix manipulation problem
Takeaways
- Boundary tracking is the most efficient approach (O(1) space)
- Four directions in fixed order: right, down, left, up
- Boundary validation prevents duplicate processing
- Layer-by-layer approach processes outer layer first
- Works for any size matrix (square or rectangular)