Matrix problems involve 2D arrays and grids. This guide covers traversal patterns, transformations, and common algorithms for matrix-based challenges.
Matrix Basics #
// Create matrix
const matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
// Access element
const val = matrix[row][col];
// Dimensions
const rows = matrix.length;
const cols = matrix[0].length;
// Iterate
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
console.log(matrix[i][j]);
}
}Matrix Traversal #
Spiral Order #
function spiralOrder(matrix) {
if (matrix.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) {
// Right
for (let col = left; col <= right; col++) {
result.push(matrix[top][col]);
}
top++;
// Down
for (let row = top; row <= bottom; row++) {
result.push(matrix[row][right]);
}
right--;
// Left
if (top <= bottom) {
for (let col = right; col >= left; col--) {
result.push(matrix[bottom][col]);
}
bottom--;
}
// Up
if (left <= right) {
for (let row = bottom; row >= top; row--) {
result.push(matrix[row][left]);
}
left++;
}
}
return result;
}
console.log(spiralOrder([[1, 2, 3], [4, 5, 6], [7, 8, 9]]));
// [1, 2, 3, 6, 9, 8, 7, 4, 5]
Spiral Matrix II #
Generate spiral matrix.
function generateMatrix(n) {
const matrix = Array(n).fill(0).map(() => Array(n).fill(0));
let top = 0;
let bottom = n - 1;
let left = 0;
let right = n - 1;
let num = 1;
while (top <= bottom && left <= right) {
// Right
for (let col = left; col <= right; col++) {
matrix[top][col] = num++;
}
top++;
// Down
for (let row = top; row <= bottom; row++) {
matrix[row][right] = num++;
}
right--;
// Left
for (let col = right; col >= left; col--) {
matrix[bottom][col] = num++;
}
bottom--;
// Up
for (let row = bottom; row >= top; row--) {
matrix[row][left] = num++;
}
left++;
}
return matrix;
}
console.log(generateMatrix(3));
// [[1, 2, 3], [8, 9, 4], [7, 6, 5]]
Diagonal Traverse #
function findDiagonalOrder(matrix) {
if (matrix.length === 0) return [];
const rows = matrix.length;
const cols = matrix[0].length;
const result = [];
let row = 0;
let col = 0;
let direction = 1; // 1 = up-right, -1 = down-left
while (result.length < rows * cols) {
result.push(matrix[row][col]);
if (direction === 1) {
// Going up-right
if (col === cols - 1) {
row++;
direction = -1;
} else if (row === 0) {
col++;
direction = -1;
} else {
row--;
col++;
}
} else {
// Going down-left
if (row === rows - 1) {
col++;
direction = 1;
} else if (col === 0) {
row++;
direction = 1;
} else {
row++;
col--;
}
}
}
return result;
}
console.log(findDiagonalOrder([[1, 2, 3], [4, 5, 6], [7, 8, 9]]));
// [1, 2, 4, 7, 5, 3, 6, 8, 9]
Matrix Transformation #
Rotate Image #
Rotate 90 degrees clockwise.
function rotate(matrix) {
const n = matrix.length;
// Transpose
for (let i = 0; i < n; i++) {
for (let j = i; j < n; j++) {
[matrix[i][j], matrix[j][i]] = [matrix[j][i], matrix[i][j]];
}
}
// Reverse each row
for (let i = 0; i < n; i++) {
matrix[i].reverse();
}
return matrix;
}
const matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
console.log(rotate(matrix));
// [[7, 4, 1], [8, 5, 2], [9, 6, 3]]
Transpose Matrix #
function transpose(matrix) {
const rows = matrix.length;
const cols = matrix[0].length;
const result = Array(cols).fill(0).map(() => Array(rows));
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
result[j][i] = matrix[i][j];
}
}
return result;
}
console.log(transpose([[1, 2, 3], [4, 5, 6]]));
// [[1, 4], [2, 5], [3, 6]]
Set Matrix Zeroes #
function setZeroes(matrix) {
const rows = matrix.length;
const cols = matrix[0].length;
let firstRowZero = false;
let firstColZero = false;
// Check first row
for (let j = 0; j < cols; j++) {
if (matrix[0][j] === 0) {
firstRowZero = true;
break;
}
}
// Check first column
for (let i = 0; i < rows; i++) {
if (matrix[i][0] === 0) {
firstColZero = true;
break;
}
}
// Use first row/col as markers
for (let i = 1; i < rows; i++) {
for (let j = 1; j < cols; j++) {
if (matrix[i][j] === 0) {
matrix[i][0] = 0;
matrix[0][j] = 0;
}
}
}
// Set zeros based on markers
for (let i = 1; i < rows; i++) {
for (let j = 1; j < cols; j++) {
if (matrix[i][0] === 0 || matrix[0][j] === 0) {
matrix[i][j] = 0;
}
}
}
// Set first row/col if needed
if (firstRowZero) {
for (let j = 0; j < cols; j++) {
matrix[0][j] = 0;
}
}
if (firstColZero) {
for (let i = 0; i < rows; i++) {
matrix[i][0] = 0;
}
}
return matrix;
}
const matrix = [[1, 1, 1], [1, 0, 1], [1, 1, 1]];
console.log(setZeroes(matrix));
// [[1, 0, 1], [0, 0, 0], [1, 0, 1]]
Matrix Search #
Search 2D Matrix #
Sorted rows and columns.
function searchMatrix(matrix, target) {
if (matrix.length === 0) return false;
const rows = matrix.length;
const cols = matrix[0].length;
// Binary search treating as 1D array
let left = 0;
let right = rows * cols - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
const row = Math.floor(mid / cols);
const col = mid % cols;
const val = matrix[row][col];
if (val === target) {
return true;
} else if (val < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return false;
}
const matrix = [[1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34, 60]];
console.log(searchMatrix(matrix, 3)); // true
console.log(searchMatrix(matrix, 13)); // false
Search 2D Matrix II #
Each row/column sorted independently.
function searchMatrix(matrix, target) {
if (matrix.length === 0) return false;
const rows = matrix.length;
const cols = matrix[0].length;
// Start from top-right
let row = 0;
let col = cols - 1;
while (row < rows && col >= 0) {
const val = matrix[row][col];
if (val === target) {
return true;
} else if (val > target) {
col--; // Move left
} else {
row++; // Move down
}
}
return false;
}
const matrix = [
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
];
console.log(searchMatrix(matrix, 5)); // true
console.log(searchMatrix(matrix, 20)); // false
Island Problems #
Number of Islands #
function numIslands(grid) {
if (grid.length === 0) return 0;
const rows = grid.length;
const cols = grid[0].length;
let count = 0;
function dfs(i, j) {
if (i < 0 || i >= rows || j < 0 || j >= cols || grid[i][j] === '0') {
return;
}
grid[i][j] = '0'; // Mark visited
dfs(i + 1, j); // Down
dfs(i - 1, j); // Up
dfs(i, j + 1); // Right
dfs(i, j - 1); // Left
}
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
if (grid[i][j] === '1') {
count++;
dfs(i, j);
}
}
}
return count;
}
const grid = [
['1', '1', '0', '0', '0'],
['1', '1', '0', '0', '0'],
['0', '0', '1', '0', '0'],
['0', '0', '0', '1', '1']
];
console.log(numIslands(grid)); // 3
Max Area of Island #
function maxAreaOfIsland(grid) {
if (grid.length === 0) return 0;
const rows = grid.length;
const cols = grid[0].length;
let maxArea = 0;
function dfs(i, j) {
if (i < 0 || i >= rows || j < 0 || j >= cols || grid[i][j] === 0) {
return 0;
}
grid[i][j] = 0;
return 1 + dfs(i + 1, j) + dfs(i - 1, j) + dfs(i, j + 1) + dfs(i, j - 1);
}
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
if (grid[i][j] === 1) {
maxArea = Math.max(maxArea, dfs(i, j));
}
}
}
return maxArea;
}
const grid = [
[0, 0, 1, 0, 0],
[0, 1, 1, 1, 0],
[0, 1, 0, 0, 0],
[1, 1, 0, 0, 1]
];
console.log(maxAreaOfIsland(grid)); // 6
Surrounded Regions #
function solve(board) {
if (board.length === 0) return;
const rows = board.length;
const cols = board[0].length;
function dfs(i, j) {
if (i < 0 || i >= rows || j < 0 || j >= cols || board[i][j] !== 'O') {
return;
}
board[i][j] = 'T'; // Temporary mark
dfs(i + 1, j);
dfs(i - 1, j);
dfs(i, j + 1);
dfs(i, j - 1);
}
// Mark border-connected 'O's
for (let i = 0; i < rows; i++) {
dfs(i, 0);
dfs(i, cols - 1);
}
for (let j = 0; j < cols; j++) {
dfs(0, j);
dfs(rows - 1, j);
}
// Flip remaining 'O's to 'X' and 'T's back to 'O'
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
if (board[i][j] === 'O') {
board[i][j] = 'X';
} else if (board[i][j] === 'T') {
board[i][j] = 'O';
}
}
}
}
const board = [
['X', 'X', 'X', 'X'],
['X', 'O', 'O', 'X'],
['X', 'X', 'O', 'X'],
['X', 'O', 'X', 'X']
];
solve(board);
console.log(board);
// [['X','X','X','X'],['X','X','X','X'],['X','X','X','X'],['X','O','X','X']]
Word Search #
function exist(board, word) {
const rows = board.length;
const cols = board[0].length;
function dfs(i, j, index) {
if (index === word.length) return true;
if (i < 0 || i >= rows || j < 0 || j >= cols || board[i][j] !== word[index]) {
return false;
}
const temp = board[i][j];
board[i][j] = '#'; // Mark visited
const found = dfs(i + 1, j, index + 1) ||
dfs(i - 1, j, index + 1) ||
dfs(i, j + 1, index + 1) ||
dfs(i, j - 1, index + 1);
board[i][j] = temp; // Restore
return found;
}
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
if (dfs(i, j, 0)) {
return true;
}
}
}
return false;
}
const board = [
['A', 'B', 'C', 'E'],
['S', 'F', 'C', 'S'],
['A', 'D', 'E', 'E']
];
console.log(exist(board, 'ABCCED')); // true
console.log(exist(board, 'SEE')); // true
console.log(exist(board, 'ABCB')); // false
Shortest Path #
Shortest Path in Binary Matrix #
function shortestPathBinaryMatrix(grid) {
const n = grid.length;
if (grid[0][0] === 1 || grid[n - 1][n - 1] === 1) {
return -1;
}
const directions = [
[-1, -1], [-1, 0], [-1, 1],
[0, -1], [0, 1],
[1, -1], [1, 0], [1, 1]
];
const queue = [[0, 0, 1]]; // [row, col, distance]
grid[0][0] = 1; // Mark visited
while (queue.length > 0) {
const [row, col, dist] = queue.shift();
if (row === n - 1 && col === n - 1) {
return dist;
}
for (const [dr, dc] of directions) {
const newRow = row + dr;
const newCol = col + dc;
if (newRow >= 0 && newRow < n && newCol >= 0 && newCol < n && grid[newRow][newCol] === 0) {
queue.push([newRow, newCol, dist + 1]);
grid[newRow][newCol] = 1;
}
}
}
return -1;
}
const grid = [[0, 1], [1, 0]];
console.log(shortestPathBinaryMatrix(grid)); // 2
Valid Sudoku #
function isValidSudoku(board) {
const rows = Array(9).fill(0).map(() => new Set());
const cols = Array(9).fill(0).map(() => new Set());
const boxes = Array(9).fill(0).map(() => new Set());
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
const val = board[i][j];
if (val === '.') continue;
const boxIndex = Math.floor(i / 3) * 3 + Math.floor(j / 3);
if (rows[i].has(val) || cols[j].has(val) || boxes[boxIndex].has(val)) {
return false;
}
rows[i].add(val);
cols[j].add(val);
boxes[boxIndex].add(val);
}
}
return true;
}Game of Life #
function gameOfLife(board) {
const rows = board.length;
const cols = board[0].length;
const directions = [
[-1, -1], [-1, 0], [-1, 1],
[0, -1], [0, 1],
[1, -1], [1, 0], [1, 1]
];
function countNeighbors(i, j) {
let count = 0;
for (const [di, dj] of directions) {
const ni = i + di;
const nj = j + dj;
if (ni >= 0 && ni < rows && nj >= 0 && nj < cols && Math.abs(board[ni][nj]) === 1) {
count++;
}
}
return count;
}
// Use 2 for dead->live, -1 for live->dead
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
const neighbors = countNeighbors(i, j);
if (board[i][j] === 1) {
if (neighbors < 2 || neighbors > 3) {
board[i][j] = -1;
}
} else {
if (neighbors === 3) {
board[i][j] = 2;
}
}
}
}
// Update to final state
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
if (board[i][j] > 0) {
board[i][j] = 1;
} else {
board[i][j] = 0;
}
}
}
}Time Complexity #
| Problem | Time Complexity | Space Complexity |
|---|---|---|
| Spiral Order | O(m×n) | O(1) |
| Rotate | O(n²) | O(1) |
| Set Zeroes | O(m×n) | O(1) |
| Search | O(log(m×n)) | O(1) |
| DFS/BFS | O(m×n) | O(m×n) |
Best Practices #
- Check boundaries - Avoid index out of bounds
- Use directions array - For neighbor traversal
- Mark visited - Modify in-place or use visited set
- Handle edge cases - Empty matrix, single element
- DFS vs BFS - DFS for paths, BFS for shortest
- Space optimization - Reuse first row/column for markers
- Clone when needed - Don’t modify input if required
Matrix problems are fundamental for grid-based algorithms. Master these patterns for efficient 2D array manipulation.