Coding Interview Patterns

Course Content

Coding Interview Patterns

20 sections · 146 lessons

Rotate Image


Rotate Image is the most-asked matrix problem. The version with a second matrix takes two minutes. The in-place version is the real question, and it has two good answers: a neat identity (transpose then reverse), and a direct four-way swap around each ring. Knowing both, and knowing why the identity works, is what interviewers look for.

original123456789transpose — swap across the diagonal147258369reverse each row741852963Two simple in-place passes compose into a 90° clockwise rotation.Neither pass needs a second matrix, so the whole rotation is O(1) extra space — which is the constraint these questions are really testing.
Rotation is hard to write directly and easy as two passes you already know — composing known operations is the trick worth remembering.

The problem

You get an n × n matrix of integers. Rotate it 90 degrees clockwise. You must change the matrix in place — do not create a second matrix and return it.

Example 1.

Text
input           output1 2 3           7 4 14 5 6    →      8 5 27 8 9           9 6 3

The top row 1 2 3 becomes the right column, read top to bottom.

Example 2. [[5, 1, 9, 11], [2, 4, 8, 10], [13, 3, 6, 7], [15, 14, 12, 16]] → [[15, 13, 2, 5], [14, 3, 4, 1], [12, 6, 8, 9], [16, 7, 10, 11]].

Constraints. 1 ≤ n ≤ 20, and the matrix is always square.

Clarifying questions

  • Always square? Yes. A non-square matrix cannot be rotated in place, since its shape changes.
  • Clockwise? Yes. (Anticlockwise is a common follow-up.)
  • Truly O(1) extra space? Yes — a few variables, no copy.
  • Values? Any integers; they do not affect the method.

Approach 1: the simple way

Work out where each cell goes, and write it into a new matrix. In a clockwise rotation, row r becomes column n − 1 − r, and column c becomes row c. So matrix[r][c] moves to result[c][n - 1 - r].

Python
def rotate_copy(matrix: list[list[int]]) -> None:    """Brute force: build the rotated matrix, then copy it back."""    n = len(matrix)    result = [[0] * n for _ in range(n)]    for r in range(n):        for c in range(n):            result[c][n - 1 - r] = matrix[r][c]    matrix[:] = result

Check it on the 1: it is at (0, 0) and goes to (0, 2), the top-right corner. Correct.

This is O(n²) time, which is optimal — every cell must move. But it uses O(n²) extra space for result, and the problem requires in place. Writing directly into matrix instead would overwrite values before they are moved: the 1 would land on the 3 before the 3 had been read.

The key insight

A rotation is hard to do in place, but reflections are easy — a reflection just swaps pairs of cells, and a swap never loses anything.

Two reflections in a row make a rotation. Reflect across the main diagonal (the transpose: swap matrix[r][c] with matrix[c][r]), then reflect left-to-right (reverse each row). Geometry says two reflections across lines that meet at an angle θ make a rotation by 2θ. The diagonal and the vertical centre line meet at 45 degrees, so the result is a 90-degree rotation.

You do not need the geometry to trust it. Check it on the example: transposing 1 2 3 / 4 5 6 / 7 8 9 gives 1 4 7 / 2 5 8 / 3 6 9, and reversing each row gives 7 4 1 / 8 5 2 / 9 6 3. That is the answer.

Approach 2: transpose, then reverse each row

Python
def rotate(matrix: list[list[int]]) -> None:    """Rotate 90 degrees clockwise in place: transpose, then reverse each row."""    n = len(matrix)    for r in range(n):        for c in range(r + 1, n):            # upper triangle only: each pair swaps once            matrix[r][c], matrix[c][r] = matrix[c][r], matrix[r][c]    for row in matrix:        row.reverse()

Step by step:

  1. Transpose. Visit only cells above the diagonal (c from r + 1) and swap each with its mirror below. Diagonal cells stay put.
  2. Reverse each row. row.reverse() works in place.

Dry run on the 3 × 3 example:

stepactionmatrix after (rows separated by /)
start—1 2 3 / 4 5 6 / 7 8 9
1swap (0,1) and (1,0): 2 and 41 4 3 / 2 5 6 / 7 8 9
2swap (0,2) and (2,0): 3 and 71 4 7 / 2 5 6 / 3 8 9
3swap (1,2) and (2,1): 6 and 81 4 7 / 2 5 8 / 3 6 9
4reverse each row7 4 1 / 8 5 2 / 9 6 3

Three swaps for a 3 × 3 — one per pair above the diagonal, n(n − 1)/2 in general.

Complexity. O(n²) time: the transpose touches half the cells and the reversal touches all of them, each once. O(1) extra space.

Approach 3: rotate ring by ring

The identity is elegant, but it moves every cell twice. The direct way moves each cell once. Treat the matrix as rings (layers), from the outside in. In each ring, cells move in groups of four: top → right → bottom → left → top. Save one, then shift the other three along.

Python
def rotate_layers(matrix: list[list[int]]) -> None:    """Rotate in place by cycling groups of four cells, one ring at a time."""    n = len(matrix)    for layer in range(n // 2):        first, last = layer, n - 1 - layer        for i in range(first, last):            offset = i - first            top = matrix[first][i]                                     # save top            matrix[first][i] = matrix[last - offset][first]            # left   -> top            matrix[last - offset][first] = matrix[last][last - offset] # bottom -> left            matrix[last][last - offset] = matrix[i][last]              # right  -> bottom            matrix[i][last] = top                                      # top    -> right

On the 3 × 3 there is one ring, with two groups. First the corners: 1 is saved, 7 moves to the top-left, 9 to the bottom-left, 3 to the bottom-right, and 1 to the top-right. Then the edge centres: 4 to the top, 8 to the left, 6 to the bottom, 2 to the right. Same result, 7 4 1 / 8 5 2 / 9 6 3.

Same O(n²) time and O(1) space, with each cell written once. It is harder to get right under pressure — four index expressions, each easy to mistype. Present the transpose version first and offer this as the "one pass" alternative.

All three approaches gave identical results on 300 random matrices from 0 × 0 to 8 × 8.

Edge cases

  • 1 × 1. The transpose loop does nothing and reversing a one-item row does nothing. Correct.
  • 2 × 2. One swap, then two reversals: [[1, 2], [3, 4]] becomes [[3, 1], [4, 2]].
  • Even vs odd n. For odd n the centre cell never moves; for even n there is no centre. Both approaches handle this without a special case (range(n // 2) rings).

Follow-ups

  • "Rotate anticlockwise." Transpose, then reverse the order of the rows (matrix.reverse()) instead of reversing within each row. Or reverse each row first and then transpose. (The old version of this course said "reverse the row order, then transpose" — that produces a clockwise rotation. Checked in code, it returned 7 4 1 / 8 5 2 / 9 6 3.)
  • "Rotate 180 degrees." Reverse the order of the rows, then reverse each row. No transpose needed.
  • "The matrix is m × n, not square." In-place is impossible, since the shape becomes n × m. Build a new matrix with result[c][m - 1 - r] = matrix[r][c], or in Python [list(row) for row in zip(*matrix[::-1])].

Check your understanding

0 of 2 answered

1.In the transpose-then-reverse method, where does the 4 in 1 2 3 / 4 5 6 / 7 8 9 end up?

2.Which of these rotates the matrix anticlockwise?