Image Matrix 90-Degree Twist

AS
Ackshaey Singh
Difficulty Hard
Time Complexity
O(n^2)
Space Complexity
O(1)
Math Array Matrix
Adobe Google Meta IBM Yahoo Oracle Capital One Tinkoff Amazon Zoho Apple Uber Cisco Walmart Labs DE Shaw ZScaler AMD Microsoft Bloomberg

You're twenty minutes into a Google interview, and the interviewer pulls up a grid of numbers on the screen. "Imagine this is a pixel grid for an image," they say. "Rotate it 90 degrees clockwise without using extra memory." The room goes quiet for a moment while you sketch out the matrix on your notepad. This problem looks deceptively simple, but the "without extra memory" constraint is what separates a quick brute-force answer from the elegant in-place solution interviewers are looking for. It is a staple at companies like Adobe, Google, Meta, and Amazon, and once you see the underlying trick, you will never forget it.

TL;DR

Rotate an n x n matrix 90 degrees clockwise in-place with two passes: first transpose the matrix by swapping matrix[i][j] with matrix[j][i] along the main diagonal, then reverse each row. This runs in O(n^2) time since every element is visited a constant number of times, and O(1) space because only a single temporary variable is needed for swapping. The key is recognizing that transpose + row reversal composes to a 90-degree clockwise rotation.

Why This Problem Matters

Matrix rotation sits at the intersection of arrays, math, and spatial reasoning. In practice, it shows up in image processing, game development (rotating sprites or tilemaps), and anywhere you need to manipulate 2D grids. In an interview context, it tests whether you can think about coordinate transformations and translate them into clean, bug-free code. It also tests in-place algorithm design, a skill that matters whenever memory is constrained.

Understanding the Problem

You are given an n x n 2D matrix of integers. Your task is to rotate every element 90 degrees clockwise, and you must do it in-place without allocating a second matrix.

Given: An n x n matrix Task: Rotate it 90 degrees clockwise, modifying the original matrix Return: The rotated matrix (for testing verification) Constraint: O(1) extra space

Here is the 3x3 example:

Loading visualization...

After a 90-degree clockwise rotation, the bottom-left corner moves to the top-left, the top-left moves to the top-right, and so on:

Loading visualization...

Notice how each row of the original becomes a column in the result, but in reverse order. The first row [1, 2, 3] becomes the last column, the second row [4, 5, 6] becomes the middle column, and the third row [7, 8, 9] becomes the first column.

Edge Cases to Consider

  1. 1x1 matrix: Already rotated, return as-is
  2. 2x2 matrix: Four elements swap in a cycle
  3. Even vs. odd dimensions: The center element of an odd-sized matrix stays put
  4. Negative values: The algorithm works on any integer values, so negative numbers require no special handling

Solution Approach

The brute-force solution would allocate a new matrix and copy each element to its rotated position. That works, but uses O(n^2) extra space. The constraint says we need O(1) space.

The observation that unlocks the in-place solution: a 90-degree clockwise rotation is equivalent to two simpler operations applied back-to-back.

  1. Transpose the matrix (swap elements across the main diagonal)
  2. Reverse each row

Why does this work? Transposing sends element (i, j) to position (j, i). Reversing row j then sends it to position (j, n-1-i). If you check the math, the mapping (i, j) -> (j, n-1-i) is exactly a 90-degree clockwise rotation.

Loading visualization...

Step 1: Transpose

Transposing means swapping matrix[i][j] with matrix[j][i] for every pair where j > i. You only need to iterate over the upper triangle of the matrix to avoid swapping each pair twice.

For our 3x3 example, the pairs that get swapped are: (0,1) with (1,0), (0,2) with (2,0), and (1,2) with (2,1). Elements on the diagonal stay in place.

Loading visualization...

Green cells sit on the diagonal and did not move. Purple cells were swapped with their mirror across the diagonal. Notice how the rows of the original matrix have become columns.

Step 2: Reverse Each Row

After transposing, reverse each row. For each row, swap the element at index j with the element at index n-1-j, iterating j from 0 to n/2.

Loading visualization...

The highlighted cells at the edges of each row were swapped. The center column stays in place for odd-sized matrices. And there it is, the fully rotated matrix, with no extra memory allocated.

Implementation

Prefer a different language? Jump to solutions in other languages.

Here is the Java solution:

public class Solution {
  public int[][] rotate(int[][] matrix) {
    int n = matrix.length;

    // Step 1: Transpose the matrix
    for (int i = 0; i < n; i++) {
      for (int j = i; j < n; j++) {
        int temp = matrix[i][j];
        matrix[i][j] = matrix[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];
        matrix[i][n - 1 - j] = temp;
      }
    }

    return matrix;
  }
}

The structure is straightforward: two nested loops for the transpose, two nested loops for the row reversal. Each loop does a simple swap.

Tracing Through the 4x4 Example

For the 4x4 matrix, the same algorithm applies without any changes. Here is the input:

Loading visualization...

And after running the transpose + reverse:

Loading visualization...

The corner elements are highlighted to make it easy to track: 5 moved from (0,0) to (0,3), 11 moved from (0,3) to (3,3), 16 stayed at (3,3) becoming (3,3) only after going through (3,3) -> (3,0) -> ah, let me be precise. The element 15 at (3,0) moved to (0,0), and 5 at (0,0) moved to (0,3). You can verify each corner follows the rotation pattern.

Complexity Analysis

Time Complexity: O(n^2)

The transpose step iterates over roughly n(n-1)/2 pairs in the upper triangle. The reverse step iterates over n * (n/2) elements. Both are proportional to n^2. Since every one of the n^2 elements must move to a new position, O(n^2) is the best we can achieve.

Space Complexity: O(1)

The only extra variable is temp, used during each swap. No auxiliary arrays, hash maps, or recursive call stacks are involved. This constant-space requirement is what makes the problem interesting.

Alternative Approaches

  1. Allocate a new matrix: Copy element (i, j) to position (j, n-1-i) in a new matrix. O(n^2) time but O(n^2) space. Does not satisfy the in-place constraint.
  2. Four-way cyclic swap: For each cell in the top-left quadrant, rotate four corresponding cells simultaneously. Same O(n^2) time and O(1) space, but the index arithmetic is trickier and more prone to off-by-one errors.
  3. Reverse rows then transpose: This produces a 90-degree counterclockwise rotation instead. The order of operations matters.

Common Pitfalls

  1. Transposing the full matrix instead of the upper triangle: If you loop both i and j from 0 to n-1, you swap every pair twice, leaving the matrix unchanged. The inner loop must start at j = i (or j = i+1 in some implementations).

    // Wrong: swaps every element twice, matrix ends up unchanged
    for (int i = 0; i < n; i++) {
      for (int j = 0; j < n; j++) {
        int temp = matrix[i][j];
        matrix[i][j] = matrix[j][i];
        matrix[j][i] = temp;
      }
    }
    
  2. Reversing the wrong axis: Reversing columns instead of rows produces a counterclockwise rotation. Double-check by tracing a corner element.

  3. Off-by-one in the reverse loop: The inner loop should run from j = 0 to j < n/2. Using j <= n/2 for odd-sized matrices swaps the middle element with itself, which is harmless, but for even-sized matrices it could cause a double-swap.

  4. Confusing clockwise with counterclockwise: Transpose + reverse rows = clockwise. Reverse rows + transpose = counterclockwise. A quick test with a 2x2 matrix confirms which is which.

Interview Tips

When this problem comes up in an interview:

  1. Start with the brute-force: Mention that you could allocate a new matrix and copy elements to their rotated positions. This shows you understand the problem before optimizing.

  2. Explain the two-step insight: "A 90-degree clockwise rotation is the same as transposing and then reversing each row." Deriving this on the whiteboard earns strong marks.

  3. Trace a small example: Walk through a 3x3 matrix step by step. Show the state after transpose and again after row reversal. This catches bugs before they make it into code.

  4. Know the variants: If the interviewer asks about counterclockwise rotation, you can swap the operation order (reverse first, then transpose) or transpose then reverse columns. For 180-degree rotation, apply the 90-degree rotation twice or reverse the matrix vertically then horizontally.

  5. Mention the four-way swap alternative: Even if you go with transpose + reverse, showing awareness of the cyclic approach demonstrates depth.

Key Takeaways

  • A 90-degree clockwise rotation equals transpose + reverse each row. This two-step decomposition avoids allocating extra memory and keeps space at O(1).
  • The transpose step must only iterate the upper triangle (j starts at i, not 0). Iterating the full matrix swaps each pair twice and leaves it unchanged.
  • Time complexity is O(n^2) and cannot be improved, because every element in the n x n matrix must be relocated.
  • For counterclockwise rotation, swap the order: reverse each row first, then transpose. Knowing both variants shows interviewers you understand the underlying linear algebra.
  • Always trace through a small example (3x3 is ideal) before coding. The index math in matrix problems is where most bugs hide.

Solutions in Other Languages

Python

class Solution:
    def rotate(self, matrix: list[list[int]]) -> list[list[int]]:
        n = len(matrix)

        # Transpose
        for i in range(n):
            for j in range(i, n):
                matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]

        # Reverse each row
        for i in range(n):
            matrix[i].reverse()

        return matrix

Python's tuple swap makes the transpose step particularly concise. The built-in reverse() method handles row reversal in-place.

JavaScript

class Solution {
  rotate(matrix) {
    const n = matrix.length;

    // Transpose
    for (let i = 0; i < n; i++) {
      for (let j = i; j < n; j++) {
        [matrix[i][j], matrix[j][i]] = [matrix[j][i], matrix[i][j]];
      }
    }

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

    return matrix;
  }
}

Destructuring assignment handles the swap cleanly, and Array.prototype.reverse() reverses each row in-place.

TypeScript

class Solution {
  rotate(matrix: number[][]): number[][] {
    const n = matrix.length;

    for (let i = 0; i < n; i++) {
      for (let j = i; j < n; j++) {
        [matrix[i][j], matrix[j][i]] = [matrix[j][i], matrix[i][j]];
      }
    }

    for (let i = 0; i < n; i++) {
      matrix[i].reverse();
    }

    return matrix;
  }
}

Identical logic to the JavaScript solution with type annotations added.

C++

#include <algorithm>
#include <vector>

class Solution {
public:
  std::vector<std::vector<int>> rotate(std::vector<std::vector<int>> &matrix) {
    int n = matrix.size();

    // Transpose
    for (int i = 0; i < n; ++i) {
      for (int j = i + 1; j < n; ++j) {
        std::swap(matrix[i][j], matrix[j][i]);
      }
    }

    // Reverse each row
    for (int i = 0; i < n; ++i) {
      std::reverse(matrix[i].begin(), matrix[i].end());
    }

    return matrix;
  }
};

The C++ solution uses std::swap and std::reverse from the standard library. Note that the inner loop starts at j = i + 1 rather than j = i to skip the self-swap on the diagonal.

Go

func (s *Solution) Rotate(matrix [][]int) [][]int {
    n := len(matrix)

    // Transpose
    for i := 0; i < n; i++ {
        for j := i; j < n; j++ {
            matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
        }
    }

    // Reverse each row
    for i := 0; i < n; i++ {
        for j := 0; j < n/2; j++ {
            matrix[i][j], matrix[i][n-j-1] = matrix[i][n-j-1], matrix[i][j]
        }
    }

    return matrix
}

Go does not have a built-in slice reverse, so the row reversal uses a manual two-pointer swap.

Scala

class Solution {
  def rotate(matrix: Array[Array[Int]]): Array[Array[Int]] = {
    val n = matrix.length

    for (i <- 0 until n) {
      for (j <- i until n) {
        val temp = matrix(i)(j)
        matrix(i)(j) = matrix(j)(i)
        matrix(j)(i) = temp
      }
    }

    for (i <- 0 until n) {
      for (j <- 0 until n / 2) {
        val temp = matrix(i)(j)
        matrix(i)(j) = matrix(i)(n - 1 - j)
        matrix(i)(n - 1 - j) = temp
      }
    }

    matrix
  }
}

Uses mutable Array[Array[Int]] for in-place modification. The range 0 until n is exclusive on the upper bound, matching the required loop boundaries.

Kotlin

class Solution {
  fun rotate(matrix: Array<IntArray>): Array<IntArray> {
    val n = matrix.size

    for (i in 0 until n) {
      for (j in i until n) {
        val temp = matrix[i][j]
        matrix[i][j] = matrix[j][i]
        matrix[j][i] = temp
      }
    }

    for (i in 0 until n) {
      for (j in 0 until n / 2) {
        val temp = matrix[i][j]
        matrix[i][j] = matrix[i][n - 1 - j]
        matrix[i][n - 1 - j] = temp
      }
    }

    return matrix
  }
}

Ruby

class Solution
  def rotate(matrix)
    n = matrix.length

    (0...n).each do |i|
      (i...n).each do |j|
        matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
      end
    end

    (0...n).each do |i|
      (0...(n / 2)).each do |j|
        matrix[i][j], matrix[i][n - 1 - j] = matrix[i][n - 1 - j], matrix[i][j]
      end
    end

    matrix
  end
end

Ruby's parallel assignment handles swaps neatly. The triple-dot range (0...n) is exclusive, matching the loop boundaries.

Rust

pub fn rotate(mut matrix: Vec<Vec<i32>>) -> Vec<Vec<i32>> {
    let n = matrix.len();

    for i in 0..n {
        for j in i..n {
            let temp = matrix[i][j];
            matrix[i][j] = matrix[j][i];
            matrix[j][i] = temp;
        }
    }

    for i in 0..n {
        for j in 0..n / 2 {
            let temp = matrix[i][j];
            matrix[i][j] = matrix[i][n - 1 - j];
            matrix[i][n - 1 - j] = temp;
        }
    }

    matrix
}

Rust requires the mut keyword since we modify the matrix in-place. The borrow checker is satisfied because each swap accesses distinct indices.

Swift

class Solution {
    func rotate(_ matrix: [[Int]]) -> [[Int]] {
        var matrix = matrix
        let n = matrix.count

        for i in 0..<n {
            for j in i..<n {
                let temp = matrix[i][j]
                matrix[i][j] = matrix[j][i]
                matrix[j][i] = temp
            }
        }

        for i in 0..<n {
            for j in 0..<n / 2 {
                let temp = matrix[i][j]
                matrix[i][j] = matrix[i][n - 1 - j]
                matrix[i][n - 1 - j] = temp
            }
        }

        return matrix
    }
}

Swift arrays are value types, so the var matrix = matrix line creates a mutable local copy. The half-open range 0..<n matches the required loop bounds.

C#

public class Solution {
    public int[][] rotate(int[][] matrix) {
        var n = matrix.Length;

        for (var i = 0; i < n; i++) {
            for (var j = i; j < n; j++) {
                var temp = matrix[i][j];
                matrix[i][j] = matrix[j][i];
                matrix[j][i] = temp;
            }
        }

        for (var i = 0; i < n; i++) {
            for (var j = 0; j < n / 2; j++) {
                var temp = matrix[i][j];
                matrix[i][j] = matrix[i][n - 1 - j];
                matrix[i][n - 1 - j] = temp;
            }
        }

        return matrix;
    }
}

Dart

class Solution {
  List<List<int>> rotate(List<List<int>> matrix) {
    int n = matrix.length;

    for (int i = 0; i < n; i++) {
      for (int j = i; j < n; j++) {
        int temp = matrix[i][j];
        matrix[i][j] = matrix[j][i];
        matrix[j][i] = temp;
      }
    }

    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];
        matrix[i][n - 1 - j] = temp;
      }
    }

    return matrix;
  }
}

Dart uses ~/ for integer division, ensuring the loop bound is computed correctly for both even and odd matrix sizes.

PHP

class Solution {
    public function rotate(array $matrix): array {
        $n = count($matrix);

        for ($i = 0; $i < $n; $i++) {
            for ($j = $i; $j < $n; $j++) {
                $temp = $matrix[$i][$j];
                $matrix[$i][$j] = $matrix[$j][$i];
                $matrix[$j][$i] = $temp;
            }
        }

        for ($i = 0; $i < $n; $i++) {
            $matrix[$i] = array_reverse($matrix[$i]);
        }

        return $matrix;
    }
}

PHP's array_reverse creates a new array, so this is not strictly in-place at the row level. For a fully in-place version, replace it with a manual swap loop.

Practice Makes Perfect

Once you are comfortable with matrix rotation, these related problems are natural next steps:

  • Spiral Matrix - Traverse a matrix in spiral order, which uses similar boundary-tracking logic
  • Set Matrix Zeroes - Another in-place matrix transformation with constrained space
  • Transpose Matrix - Practice just the transpose step on non-square matrices
  • Rotate Array - Apply the reversal trick to a 1D array

Remember, consistent practice is the key to succeeding in coding interviews. This problem and thousands of others are available on Firecode, where over 50,000 users have successfully prepared for technical interviews and landed six and seven-figure jobs at top tech companies. Whether you are just starting or preparing for your next big interview, building fluency with matrix manipulation will pay dividends.

Happy coding, and may all your rotations land right-side up!

Frequently Asked Questions

What is the transpose-then-reverse approach for rotating a matrix 90 degrees clockwise?
First, transpose the matrix by swapping every element at position (i, j) with the element at (j, i) along the main diagonal. Then reverse each row from left to right. The combination of these two operations produces a 90-degree clockwise rotation. Both steps modify the matrix in-place, so total space complexity is O(1).
Why does transposing and then reversing rows produce a 90-degree clockwise rotation?
Transposing flips the matrix along its main diagonal, turning rows into columns. Reversing each row then mirrors the columns left-to-right. Algebraically, an element at position (i, j) moves to (j, i) after transpose, then to (j, n-1-i) after row reversal. The mapping (i, j) to (j, n-1-i) is exactly the definition of a 90-degree clockwise rotation.
What is the time complexity of rotating a matrix in-place?
O(n^2) where n is the side length of the square matrix. The transpose step visits roughly n^2/2 elements (the upper triangle), and the reverse step touches all n^2 elements. Both are proportional to n^2, giving an overall O(n^2) time complexity. This is optimal because every element must move to its new position.
What is the space complexity of the transpose-then-reverse approach?
O(1) because the algorithm only uses a single temporary variable for swapping. No additional matrix or data structure is allocated. The rotation happens entirely within the original matrix, which is what the problem requires.
How do you rotate a matrix 90 degrees counterclockwise instead?
Reverse each row first, then transpose. Alternatively, transpose the matrix and then reverse each column instead of each row. The counterclockwise mapping sends element (i, j) to (n-1-j, i), which is achieved by either of these two-step combinations.
How do you rotate a matrix by 180 degrees?
Apply the 90-degree clockwise rotation twice. A simpler approach is to reverse the entire matrix top-to-bottom, then reverse each row left-to-right. You can also swap element (i, j) directly with element (n-1-i, n-1-j) in a single pass over half the matrix.
Can you rotate a non-square matrix 90 degrees in-place?
Not truly in-place, because a 90-degree rotation of an m x n matrix produces an n x m matrix with different dimensions. You would need to allocate a new matrix of size n x m. The in-place transpose-then-reverse trick only works for square (n x n) matrices where the dimensions remain unchanged.
What is the four-way swap approach for rotating a matrix?
Instead of transpose + reverse, you can rotate four corresponding elements at once in a single pass. For each cell (i, j) in the top-left quadrant, cycle the values at (i, j), (j, n-1-i), (n-1-i, n-1-j), and (n-1-j, i). This approach also runs in O(n^2) time and O(1) space but is harder to implement correctly during an interview.
How often does the Rotate Image problem appear in coding interviews?
Rotate Image is one of the most frequently asked matrix problems in 2026 interviews, especially at companies like Adobe, Google, Meta, Yahoo, and Amazon. It tests your ability to manipulate 2D arrays in-place and understand geometric transformations, skills that transfer to image processing and computer graphics questions.
What are common mistakes when implementing matrix rotation?
The most common mistake is transposing the full matrix instead of just the upper triangle, which swaps elements twice and leaves the matrix unchanged. Another frequent bug is using the wrong loop bounds when reversing rows, leading to double-reversal. Always trace through a small 3x3 example to verify your index math before coding.