Rotate Image: In-Place Matrix Rotation
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.
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:
- Transpose the matrix (swap
matrix[i][j]withmatrix[j][i]for alliandj). - 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.
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].
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.
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
| Metric | Value |
|---|---|
| Time | O(n^2) |
| Space | O(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]andmatrix[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.