Problem Analysis
The spiral matrix traversal problem requires printing matrix elements in a clockwise spiral pattern, starting from the top-left corner and moving inward layer by layer. The traversal follows a consistent pattern: move right along the top edge, then down the right edge, then left along the bottom edge, and finally up the left edge before transitioning to the next inner layer.
Solution Approaches
Direction-Based Traevrsal
This approach uses a direction matrix to control movement and a visited array to track traversed positions.
function spiralTraverse(matrix) {
if (!matrix || matrix.length === 0) {
return [];
}
const numRows = matrix.length;
const numCols = matrix[0].length;
const visited = Array.from({ length: numRows }, () => new Array(numCols).fill(false));
const result = new Array(numRows * numCols);
const dirX = [0, 1, 0, -1];
const dirY = [1, 0, -1, 0];
let currentDir = 0;
let x = 0, y = 0;
for (let i = 0; i < result.length; i++) {
result[i] = matrix[x][y];
visited[x][y] = true;
const nextX = x + dirX[currentDir];
const nextY = y + dirY[currentDir];
if (nextX < 0 || nextX >= numRows || nextY < 0 || nextY >= numCols || visited[nextX][nextY]) {
currentDir = (currentDir + 1) % 4;
}
x += dirX[currentDir];
y += dirY[currentDir];
}
return result;
}
The core mechanism relies on four direction vectors. When the next position is either out of bounds or already visited, the direction switches to the next one in sequence. This ensures automatic boundary handling without explicit edge-case management.
Boundary-Based Traversal
This method uses four boundary pointers to define the currrent spiral layer.
function spiralTraverse(matrix) {
if (!matrix || matrix.length === 0) {
return [];
}
const rows = matrix.length;
const cols = matrix[0].length;
const result = [];
let left = 0, right = cols - 1;
let top = 0, bottom = rows - 1;
while (left <= right && top <= bottom) {
// Traverse right along the top row
for (let col = left; col <= right; col++) {
result.push(matrix[top][col]);
}
// Traverse down along the right column
for (let row = top + 1; row <= bottom; row++) {
result.push(matrix[row][right]);
}
// Handle middle layers: traverse left along the bottom row
if (left < right && top < bottom) {
for (let col = right - 1; col > left; col--) {
result.push(matrix[bottom][col]);
}
for (let row = bottom; row > top; row--) {
result.push(matrix[row][left]);
}
}
// Shrink boundaries for the next layer
left++;
right--;
top++;
bottom--;
}
return result;
}
The boundary pointers shrink inward after completing each layer. The conditional check prevents duplicate printing when the matrix has only one row or one column remaining.
Java Implementation
class Solution {
public int[] spiralOrder(int[][] matrix) {
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
return new int[0];
}
int rows = matrix.length;
int cols = matrix[0].length;
int[] result = new int[rows * cols];
int pos = 0;
int left = 0, right = cols - 1;
int top = 0, bottom = rows - 1;
while (left <= right && top <= bottom) {
for (int c = left; c <= right; c++) {
result[pos++] = matrix[top][c];
}
for (int r = top + 1; r <= bottom; r++) {
result[pos++] = matrix[r][right];
}
if (left < right && top < bottom) {
for (int c = right - 1; c > left; c--) {
result[pos++] = matrix[bottom][c];
}
for (int r = bottom; r > top; r--) {
result[pos++] = matrix[r][left];
}
}
left++;
right--;
top++;
bottom--;
}
return result;
}
}
Complexity Analysis
Both approaches traverse each matrix element exactly once, resulting in O(m × n) time complexity where m represents rows and n represents columns. The direction-based method uses an additional visited array of the same size, while the boundary-based method only uses O(1) extra space besides the result array.
Key Considerations
The boundary-based approach requires careful handling of edge cases where rows or columns equal one. The direction-based approach handles these naturally through its boundary checking mechanism but incurs the memory cost of a visited array. Choosing between them depends on whether memory optimization or conceptual simplicity is prioritized for the specific use case.