Skip to content

Matrix Algorithms: Code Templates in 6 Languages

If you have not read the concept guide yet, start there for the coordinate system and the boundary rules. This page gives you the code you can memorize and adapt during an interview. Every template runs in time proportional to the number of cells,

O(M*N)
, except the rotation, which is
O(N^2)
for an n x n matrix. The space cost is stated per template.

Main Template: Grid Traversal with Boundary Checks

This is the foundation of every matrix algorithm. A double loop visits every cell once, and each cell can reach its four neighbors with a bounds check on each one. Most matrix problems start from this skeleton and add a condition inside the loop.

Use this for the traversal-based problems on the practice problems page, including Reshape the Matrix and Transpose Matrix .

function traverseGrid(grid) {
    const rows = grid.length;
    const cols = grid[0].length;
    const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];

    for (let r = 0; r < rows; r++) {
        for (let c = 0; c < cols; c++) {
            // The current cell is in bounds by construction.
            // Neighbors are not, so each candidate gets validated.
            for (const [dr, dc] of directions) {
                const nr = r + dr;
                const nc = c + dc;
                // Edges have fewer than four neighbors, so skip
                // any candidate that lands outside the grid.
                if (nr >= 0 && nr < rows && nc >= 0 && nc < cols) {
                    // process grid[nr][nc]
                }
            }
        }
    }
}
Templates only help if you can recall them without staring at the screen. Turn these snippets into flashcards for daily mobile review.

Code Breakdown

Key Variables

  • rows and cols: the grid dimensions, read once at the start. Every bounds check compares against these two values, so they stay in scope for the whole function.
  • r and c: the current cell’s row and column. The inner loop advances c to the end of the row, then resets it to 0 while r advances.
  • directions: the four neighbor offsets. One entry per direction, and every one is applied with the same bounds check.
  • nr and nc: the candidate neighbor coordinates. They are validated before any access happens.

Visual Mechanism

    graph TD
    A["Cell (r,c)"] --> B{"Neighbor inside grid?"}
    B -->|"0 <= nr < rows and 0 <= nc < cols"| C["Access grid[nr][nc]"]
    B -->|No| D["Skip candidate"]
    C --> E["Move to next direction"]
    D --> E
  

Critical Sections

The initialization reads rows and cols once and reuses them everywhere. Reading grid[0].length inside the loop is a subtle trap. It repeats work and it breaks on jagged input, where one row has a different length from the others. One read up front keeps the bounds consistent for every cell.

The neighbor loop is where the bounds check lives. Each of the four offsets is applied to the current cell, and the candidate is validated before access. Skipping that check is the single most common matrix bug, and it only fails on edge rows and columns.

The termination is implicit in the loop conditions. c advances to cols, then resets with r incrementing. When r reaches rows, every cell has been visited exactly once, so the loop cannot double-count or skip a cell.

Variations

1. Grid DFS with Visited Marking

When the problem asks you to explore a connected region, the recursive DFS form is the template to memorize. The visited marking matters more than the recursion. A cell is claimed before its neighbors are explored, so overlapping paths cannot revisit it.

Use this for flood-fill and island-counting problems. Number of Islands appears on the Union Find problems page , and Longest Increasing Path in a Matrix extends this skeleton with a memo table instead of a visited set.

function floodFill(grid, startR, startC) {
    const rows = grid.length, cols = grid[0].length;
    const visited = new Set();

    function dfs(r, c) {
        if (r < 0 || r >= rows || c < 0 || c >= cols) return;
        const key = r + "," + c;
        if (visited.has(key)) return;

        // Claim the cell before exploring so the neighbors
        // cannot schedule it again through another path.
        visited.add(key);

        dfs(r - 1, c);
        dfs(r + 1, c);
        dfs(r, c - 1);
        dfs(r, c + 1);
    }

    dfs(startR, startC);
}

This template runs in

O(M*N)
time and
O(M*N)
space, since the visited table mirrors the grid.

2. Spiral Traversal

Spiral order replaces loop counters with four boundaries that shrink as each side is consumed. The guards after the first two passes exist because the remaining grid can be a single row or a single column.

Use this for Spiral Matrix .

function spiralOrder(matrix) {
    const result = [];
    if (matrix.length === 0) return result;

    let top = 0, bottom = matrix.length - 1;
    let left = 0, right = matrix[0].length - 1;

    while (top <= bottom && left <= right) {
        // Right along the top row, then the top edge is done.
        for (let j = left; j <= right; j++) result.push(matrix[top][j]);
        top++;

        // Down the right column, then the right edge is done.
        for (let i = top; i <= bottom; i++) result.push(matrix[i][right]);
        right--;

        // The grid may now be a single row, so guard the pass.
        if (top <= bottom) {
            for (let j = right; j >= left; j--) result.push(matrix[bottom][j]);
            bottom--;
        }

        // The grid may now be a single column, so guard the pass.
        if (left <= right) {
            for (let i = bottom; i >= top; i--) result.push(matrix[i][left]);
            left++;
        }
    }
    return result;
}

Runs in

O(M*N)
time and
O(1)
extra space, since the result list is the only storage.

3. In-Place Rotation

A 90-degree clockwise rotation is a transpose followed by reversing each row. Transposing alone swaps the cell pairs across the diagonal. The row reversal then moves every column into its rotated position. Both steps work in place for square matrices.

Use this for Rotate Image .

function rotate(matrix) {
    const n = matrix.length;

    // Transpose: swap across the main diagonal. Only j > i
    // is needed because the swap covers both sides.
    for (let i = 0; i < n; i++) {
        for (let j = i + 1; j < n; j++) {
            [matrix[i][j], matrix[j][i]] = [matrix[j][i], matrix[i][j]];
        }
    }

    // Reverse each row to complete the clockwise rotation.
    for (let i = 0; i < n; i++) {
        matrix[i].reverse();
    }
}

Runs in

O(N^2)
time and
O(1)
extra space.

Each variation above is a separate review card in the app. Drill them individually so you do not mix up the conditions under pressure.

Now head to the practice problems to apply these templates to real interview questions.

Now that you have the templates, the next step is application. Head to the practice problems or start reviewing these templates with spaced repetition .