Skip to content
← All posts

Rotate Image: In-Place Matrix Rotation

6 min read
leetcodeproblemmediumarrays

You are given an n x n 2D matrix representing an image. Rotate the image by 90 degrees clockwise, in place. That is LeetCode 48: Rotate Image, and it is one of those problems where the brute force (copy to a new matrix) is easy, but doing it in O(1) space requires a clean trick.

Original12345678990° CWRotated741852963
A 3x3 matrix before and after 90-degree clockwise rotation. Accent = corners, amber = edges, gray = center. Each element moves to a new position, but the center stays put.

Why this problem matters

Matrix rotation shows up constantly in interviews. It tests whether you can decompose a spatial transformation into simpler operations. The problem also reinforces in-place thinking: you cannot just create a new matrix and copy values over, because the problem demands O(1) extra space.

The pattern you learn here, transpose then reverse, applies to all four rotation directions (90, 180, 270 degrees) with small variations. Once you have it down, any "rotate image" or "matrix rotation" variant becomes a two-liner.

The key insight: transpose + reverse

A 90-degree clockwise rotation is equivalent to two simpler operations performed in sequence:

  1. Transpose the matrix (swap matrix[i][j] with matrix[j][i] for all i and j).
  2. Reverse each row (flip every row left to right).

Why does this work? Transposing converts rows into columns. Reversing each row then flips the column order, which is exactly what a clockwise rotation does. If you think about where element (r, c) ends up after a 90-degree clockwise rotation, it lands at (c, n - 1 - r). Transposing moves it to (c, r), and reversing row c moves it from column r to column n - 1 - r. That is exactly the target position.

For other rotation amounts: 90 degrees counterclockwise is reverse each row, then transpose. 180 degrees is reverse each row, then reverse each column. Once you understand the transpose + reverse building block, you just swap the order or axis.

The solution

def rotate(matrix: list[list[int]]) -> None:
    n = len(matrix)

    for i in range(n):
        for j in range(i + 1, n):
            matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]

    for row in matrix:
        row.reverse()

That is it. Two loops. The first loop transposes the matrix by swapping elements above the diagonal with their mirror below the diagonal. The inner loop starts at j = i + 1 to avoid swapping each pair twice (which would undo the transpose). The second loop reverses each row in place.

Visual walkthrough

Let's trace through a 3x3 matrix step by step using the transpose-then-reverse approach.

Step 1: Original matrix.

123456789

Start with the input. We need to rotate this 90 degrees clockwise, in place.

Step 2: Transpose the matrix. Swap matrix[i][j] with matrix[j][i].

147258369

Rows become columns. (1,2,3) along the top row is now (1,2,3) down the first column. Green cells were swapped across the diagonal.

Step 3: Reverse each row. This gives the final rotated matrix.

741852963

Each row is reversed left to right. [1,4,7] becomes [7,4,1]. The result matches a 90-degree clockwise rotation.

The transpose swaps elements across the main diagonal. After that, each row is reversed to complete the rotation. Two passes, no extra space beyond a single temporary variable for the swap.

Alternative approach: layer-by-layer rotation

There is another way to rotate in place that mirrors the spiral matrix pattern. You process the matrix as concentric layers, rotating four elements at a time in a cycle.

For each layer, you pick an element from the top edge, save it, then move the left edge element into the top position, the bottom edge element into the left position, the right edge element into the bottom position, and the saved top element into the right position. Repeat for every element in the layer, then move to the next inner layer.

def rotate(matrix: list[list[int]]) -> None:
    n = len(matrix)
    for layer in range(n // 2):
        first = layer
        last = n - 1 - layer
        for i in range(first, last):
            offset = i - first
            top = matrix[first][i]
            matrix[first][i] = matrix[last - offset][first]
            matrix[last - offset][first] = matrix[last][last - offset]
            matrix[last][last - offset] = matrix[i][last]
            matrix[i][last] = top

This approach is correct and also runs in O(n^2) time with O(1) space. However, it is harder to remember and easier to get wrong because of the four index expressions. The transpose + reverse approach is simpler to code and less error-prone.

Both approaches have the same time and space complexity. In interviews, go with whichever one you can write correctly under pressure. For most people, that is transpose + reverse.

Complexity analysis

MetricValue
TimeO(n^2)
SpaceO(1)

Time: The transpose visits each pair above the diagonal once, which is n * (n - 1) / 2 swaps. Reversing all rows is another n * (n / 2) swaps. Both are O(n^2).

Space: Everything is done in place. The only extra variable is the temporary used during swaps, which is O(1).

Edge cases

  • 1x1 matrix: [[5]] stays [[5]]. The transpose loop and reverse loop both do nothing.
  • 2x2 matrix: [[1,2],[3,4]] becomes [[3,1],[4,2]]. The transpose swaps one pair (matrix[0][1] and matrix[1][0]), then each row is reversed. Small but worth verifying.
  • Even vs. odd n: both work identically. The center element of an odd-sized matrix sits on the diagonal and is never swapped during the transpose. Reversing its row moves it but only within the same row.

The building blocks

This problem is built on one core reusable piece that CodeBricks drills independently:

In-place matrix manipulation

The transpose + reverse pattern is a specific instance of a broader skill: transforming a matrix in place by decomposing the operation into simpler sub-operations.

# Transpose: swap across the diagonal
for i in range(n):
    for j in range(i + 1, n):
        matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]

# Reverse each row
for row in matrix:
    row.reverse()

This decomposition strategy shows up in multiple matrix problems:

  • Spiral Matrix (LeetCode 54): peel layers from the outside in using boundary tracking.
  • Set Matrix Zeroes (LeetCode 73): use the matrix's own first row and column as marker storage.
  • Rotate Image (LeetCode 48): transpose + reverse to achieve rotation without extra space.

The common thread is using the matrix itself as both the source and the destination, performing swaps or rearrangements in place rather than copying to a new structure. Once you are comfortable with index manipulation and diagonal-based swaps, these problems become predictable.

Common mistakes

1. Transposing the full matrix instead of just the upper triangle. If you let j start at 0 instead of i + 1, every pair gets swapped twice, and the matrix ends up unchanged. The inner loop must only visit elements above the main diagonal.

2. Reversing columns instead of rows. Transpose + reverse rows gives 90-degree clockwise. Transpose + reverse columns gives 90-degree counterclockwise. Mix them up and the result is a mirror image of what you wanted.

3. Off-by-one in the layer approach. The four-element cycle in the layer-by-layer method involves four different index expressions. Getting even one wrong rotates elements to the wrong position, and the bug is hard to spot visually.

4. Forgetting it must be in place. Creating a new matrix and returning it fails the problem constraint. The function signature returns None, and the matrix must be modified directly.

From understanding to recall

You now understand the transpose + reverse trick for matrix rotation. It is only two loops. But under interview pressure, small details slip: Does j start at i or i + 1? Do you reverse rows or columns? Do you transpose first or reverse first?

Spaced repetition locks these details in. You write the transpose loop and the row-reverse loop from scratch. You do it today, again in two days, again in five. After a few reps, you see "rotate image" and the two-step solution appears on your screen without hesitation. No second-guessing the index bounds, no confusing rows with columns.

In-place matrix manipulation is one of roughly 60 reusable building blocks that cover hundreds of LeetCode problems. Learning these individually and drilling them with spaced repetition is far more effective than grinding random problems and hoping the patterns stick.

Related posts

  • Spiral Matrix - Layer-by-layer traversal of a matrix, the same boundary-peeling pattern used in the alternative rotation approach
  • Set Matrix Zeroes - Another in-place matrix problem where you repurpose part of the input as bookkeeping space

CodeBricks breaks Rotate Image into its transpose-then-reverse building block and drills it independently with spaced repetition. You type the diagonal swap loop and the row-reverse loop from scratch until the pattern is automatic. When a matrix rotation problem shows up in your interview, you do not think about it. You just write it.