Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Rotate an n x n matrix 90 degrees clockwise in-place — decomposed into two elegant operations.
You are given an n x n 2D matrix representing an image, rotate the image by 90 degrees (clockwise).
You have to rotate the image in-place, which means you have to modify the input 2D matrix directly. DO NOT allocate another 2D matrix and do the rotation.
Example 1:
Input: matrix = [[1,2,3],[4,5,6],[7,8,9]] Output: [[7,4,1],[8,5,2],[9,6,3]]
Example 2:
Input: matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]] Output: [[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]
Constraints:
n == matrix.length == matrix[i].length1 <= n <= 20-1000 <= matrix[i][j] <= 1000The simplest approach: create a new matrix, and for each element at (i, j), place it at (j, n-1-i) in the new matrix.
This works but uses O(n^2) extra space. The problem requires in-place rotation — can we do it without a second matrix?
Key observation: A 90-degree clockwise rotation can be decomposed into two simpler operations that are each easy to do in-place: transpose then reverse each row.
A 90-degree clockwise rotation equals: transpose the matrix (swap rows and columns), then reverse each row.
matrix[i][j] with matrix[j][i] for all j > i. This mirrors across the main diagonal.Why does this work? Transposing maps (i,j) to (j,i). Reversing rows then maps (j,i) to (j, n-1-i). The combined effect: (i,j) goes to (j, n-1-i) — which is exactly the 90-degree clockwise rotation formula.
Let's watch the transformation step by step on [[1,2,3],[4,5,6],[7,8,9]].
Rows are color-coded: row 0, row 1, row 2
Elements above the diagonal (highlighted in red) swap with their mirror below. Diagonal elements (1, 5, 9) stay in place. Swaps: (0,1)↔(1,0), (0,2)↔(2,0), (1,2)↔(2,1).
Each row's first and last elements swap. Middle elements (in odd-sized rows) stay in place. Result: [[7,4,1],[8,5,2],[9,6,3]].
class Solution { public void rotate(int[][] matrix) { int n = matrix.length; // Step 1: Transpose — mirror across main diagonal // Only iterate upper triangle (j starts at i+1) for (int i = 0; i < n; i++) for (int j = i + 1; j < n; j++) { int temp = matrix[i][j]; matrix[i][j] = matrix[j][i]; // swap (i,j) with (j,i) matrix[j][i] = temp; } // Step 2: Reverse each row for (int i = 0; i < n; i++) for (int j = 0; j < n / 2; j++) { int temp = matrix[i][j]; matrix[i][j] = matrix[i][n - 1 - j]; // swap left with right matrix[i][n - 1 - j] = temp; } } }
func rotate(matrix [][]int) { n := len(matrix) // Step 1: Transpose — mirror across main diagonal // Only iterate upper triangle (j starts at i+1) for i := 0; i < n; i++ { for j := i + 1; j < n; j++ { matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j] // swap (i,j) with (j,i) } } // Step 2: Reverse each row for i := 0; i < n; i++ { for j := 0; j < n/2; j++ { matrix[i][j], matrix[i][n-1-j] = matrix[i][n-1-j], matrix[i][j] // swap left with right } } }
def rotate(matrix: list[list[int]]) -> None: n = len(matrix) # Step 1: Transpose — mirror across main diagonal # Only iterate upper triangle (j starts at i+1) for i in range(n): for j in range(i + 1, n): matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j] # swap (i,j) with (j,i) # Step 2: Reverse each row for i in range(n): matrix[i].reverse()
impl Solution { pub fn rotate(matrix: &mut Vec<Vec<i32>>) { let n = matrix.len(); // Step 1: Transpose — mirror across main diagonal // Only iterate upper triangle (j starts at i+1) for i in 0..n { for j in (i+1)..n { let tmp = matrix[i][j]; matrix[i][j] = matrix[j][i]; // swap (i,j) with (j,i) matrix[j][i] = tmp; } } // Step 2: Reverse each row for i in 0..n { matrix[i].reverse(); // swap left with right } } }
function rotate(matrix: number[][]): void { const n = matrix.length; // Step 1: Transpose — mirror across main diagonal // Only iterate upper triangle (j starts at i+1) 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]]; // swap (i,j) with (j,i) // Step 2: Reverse each row for (let i = 0; i < n; i++) matrix[i].reverse(); }
Choose a matrix size and step through the transpose and reverse operations. Watch the cells swap positions in real-time.
We visit each element a constant number of times: once during transpose, once during row reversal. That is O(n2) total operations for an n × n matrix. No extra data structures -- just a single temp variable for swapping.
Allocate a second n × n matrix and copy each element from (i, j) to (j, n-1-i). Two nested loops visit all n2 cells once. The extra matrix doubles memory usage, which the problem explicitly disallows.
Transpose swaps n(n-1)/2 pairs above the diagonal; reversing each row swaps n/2 pairs per row. Both passes touch every element a constant number of times for O(n2) total. Only a single temp variable is needed -- no extra matrix.
"Transpose then reverse" is an algebraic decomposition of 90-degree rotation -- each element moves exactly once per step. Any rotation must move O(n2) elements, so the time is optimal. The O(1) space is strictly better than allocating a second matrix.
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: Temporary multiplications exceed integer bounds.
Usually fails on: Large inputs wrap around unexpectedly.
Fix: Use wider types, modular arithmetic, or rearranged operations.