Flip an image on its horizontal axis

AS
Ackshaey Singh
Difficulty Easy
Time Complexity
O(m * n)
Space Complexity
O(m * n)
Matrix
Bloomberg

You're working through a Bloomberg phone screen when the interviewer pulls up a shared editor and says, "Imagine you have a digital photo stored as a grid of pixel values. How would you flip it upside down?" The question sounds deceptively simple, but it tests your comfort with 2D array indexing and your ability to write clean, correct code under pressure. This problem, also known as "Flip an Image Vertically" on other platforms, is a staple warm-up question at companies like Bloomberg.

TL;DR

Reverse the order of rows in a 2D array to flip the image on its horizontal axis. For each index i from 0 to image.length - 1, place image[image.length - 1 - i] into position i of a new result array. This runs in O(m * n) time and O(m * n) space, where m is the number of rows and n the number of columns. In Java, you can express this concisely with IntStream.range().mapToObj().toArray().

Why This Problem Matters

Matrix manipulation problems are everywhere in technical interviews, and this one is a perfect entry point. It builds the mental model for working with 2D arrays, index arithmetic, and the distinction between mutating data in-place versus returning a new copy. Once you're comfortable flipping rows, you'll find that related problems like matrix rotation, transposition, and spiral traversal become much more approachable.

Understanding the Problem

We're given a black and white image represented as a 2D array of integers (values between 0 and 255, where 0 is black and 255 is white). Our job is to flip the image on its horizontal axis, which means turning it upside down. The top row becomes the bottom row, and the bottom row becomes the top.

Here's a concrete example. Given:

flipImage([[255,0,0],[0,0,255]]) -> [[0,0,255],[255,0,0]]

The first row [255, 0, 0] moves to the bottom, and the second row [0, 0, 255] moves to the top.

Let's visualize a slightly larger example. Consider the grayscale image [[255, 0, 0], [0, 0, 255]]:

Original image:

Loading visualization...

After flipping on the horizontal axis:

Loading visualization...

Notice that each row keeps its internal values intact. Only the ordering of rows changes. This is the key distinction between a horizontal flip (rows reversed) and a vertical flip (columns reversed within each row).

Edge Cases to Consider

  1. Empty image (zero rows): Return an empty array
  2. Single row: The flipped version is identical to the input
  3. Empty rows (like [[]]): Return the same structure
  4. Square vs. rectangular: The algorithm works the same regardless of dimensions

Solution Approach

The core idea is straightforward: build a new 2D array where row i of the result holds row image.length - 1 - i of the original.

The Index Mapping

For an image with 3 rows, the mapping looks like this:

Loading visualization...

Row 0 of the original becomes row 2 of the result. Row 1 stays in place. Row 2 moves to position 0. The general formula is:

result[i] = image[image.length - 1 - i]

This works for any number of rows. When the image has an odd number of rows, the middle row maps to itself.

Walking Through a 3x3 Example

Let's trace through [[1,2,3],[4,5,6],[7,8,9]]:

Original:

Loading visualization...

We create a new array and fill it using our index mapping:

  • result[0] = image[2] which is [7, 8, 9]
  • result[1] = image[1] which is [4, 5, 6]
  • result[2] = image[0] which is [1, 2, 3]

Flipped:

Loading visualization...

The image is now upside down. Rows that were at the top are now at the bottom.

Implementation

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

Here's the Java solution using streams for a clean, functional approach:

import java.util.stream.IntStream;

public class Solution {
  public int[][] flipImage(int[][] image) {
    // Create a stream of indices from 0 to image.length - 1
    // For each index i, grab the row at position image.length - 1 - i
    // Collect all rows into a new 2D array
    return IntStream
             .range(0, image.length)
             .mapToObj(i -> image[image.length - 1 - i])
             .toArray(int[][]::new);
  }
}

Let's break down what's happening:

  1. IntStream.range(0, image.length) generates indices [0, 1, 2, ..., n-1]
  2. .mapToObj(i -> image[image.length - 1 - i]) maps each index to its corresponding row from the end
  3. .toArray(int[][]::new) collects the mapped rows into a new int[][]

This functional approach avoids explicit loops and temporary variables, making the intent clear in a single expression.

The Imperative Alternative

If you prefer a traditional loop-based approach, or your interviewer asks you to avoid streams:

public class Solution {
  public int[][] flipImage(int[][] image) {
    int rows = image.length;
    int[][] result = new int[rows][];
    for (int i = 0; i < rows; i++) {
      result[i] = image[rows - 1 - i];
    }
    return result;
  }
}

Both approaches produce the same result. The loop version is arguably easier to trace through during a whiteboard session, while the stream version is more idiomatic modern Java.

Complexity Analysis

Time Complexity: O(m * n)

We iterate through every row and copy a reference to it in the new array. The row reference copy itself is O(1), but since we're producing a result that contains all m * n elements, the overall complexity is O(m * n). If you needed to deep-copy each row (creating entirely independent arrays), you'd pay O(n) per row for the copy, but the total is still O(m * n).

Space Complexity: O(m * n)

We allocate a new 2D array to hold the flipped result. This uses O(m * n) space for the result array. Note that our Java solution shares row references between the original and result arrays rather than deep-copying. If immutability is a concern, you'd deep-copy each row, but the asymptotic space stays the same.

Could We Do Better on Space?

If the problem allowed in-place modification, we could swap rows from the top and bottom, working inward. That would reduce extra space to O(1). But the problem explicitly asks us to return a new copy without modifying the input, so O(m * n) space is the best we can achieve.

Common Pitfalls

  1. Confusing horizontal and vertical flips: A horizontal flip reverses row order. A vertical flip reverses column order within each row. Read the problem statement carefully.

  2. Modifying the original array: The problem says to return a copy. If you swap rows in-place, you've violated this constraint.

  3. Off-by-one in the index formula: The last valid index is image.length - 1, not image.length. Getting this wrong produces an ArrayIndexOutOfBoundsException.

  4. Forgetting edge cases: An empty input array should return an empty array, not throw a null pointer exception. Test with [[]] as well.

Interview Tips

When this problem comes up in an interview:

  1. Clarify what "horizontal axis" means: Ask whether it means reversing rows (upside down) or reversing columns (mirror). Most interviewers mean row reversal, but it's worth confirming.

  2. Ask about in-place vs. copy: The problem says "return a copy," but confirming this shows attention to detail.

  3. Start with the formula: Write result[i] = image[n - 1 - i] on the whiteboard before coding. It makes the implementation almost trivial.

  4. Mention the in-place alternative: Even though the problem doesn't require it, briefly noting that you could swap rows in O(1) space shows deeper understanding.

  5. Consider follow-ups: The interviewer might ask you to flip vertically, rotate 90 degrees, or transpose the matrix. Having the 2D index arithmetic fresh in your mind makes these extensions natural.

Key Takeaways

  • Flipping an image on its horizontal axis is equivalent to reversing the order of rows in a 2D array. The formula result[i] = image[n - 1 - i] captures the entire logic.
  • Java's IntStream provides a clean functional way to express this transformation without explicit loops.
  • Time and space are both O(m * n) because you visit every element and produce a full copy. In-place row swapping can reduce space to O(1) if the problem allows mutation.
  • This problem is a building block for harder matrix operations like 90-degree rotation (transpose + flip) and spiral traversal.
  • Always confirm whether "flip" means row reversal or column reversal, and whether you should modify in-place or return a new array.

Solutions in Other Languages

Python

Python makes this particularly concise with slice notation:

from typing import List


class Solution:
    def flip_image(self, image: List[List[int]]) -> List[List[int]]:
        return image[::-1]

The [::-1] slice creates a shallow copy of the list with elements in reverse order. Since each element is a row (a list), this reverses the row ordering.

JavaScript

class Solution {
  flipImage(image) {
    return image.map((row, i) => image[image.length - 1 - i]);
  }
}

The map callback ignores the current row and instead picks the row from the opposite end of the array.

TypeScript

class Solution {
  flipImage(image: number[][]): number[][] {
    return image.map((_, i) => image[image.length - 1 - i]);
  }
}

Same approach as JavaScript with type annotations added.

C++

#include <vector>

class Solution {
public:
  std::vector<std::vector<int>> flipImage(std::vector<std::vector<int>> image) {
    if (image.empty()) return {};
    auto rows = image.size();
    std::vector<std::vector<int>> reversedImage;
    reversedImage.resize(rows, std::vector<int>(image[0].size()));
    for (auto i = 0; i < rows; i++) {
      reversedImage[rows - 1 - i] = image[i];
    }
    return reversedImage;
  }
};

Go

func FlipImage(image [][]int) [][]int {
    rows := len(image)
    reversedImage := make([][]int, rows)
    for i, row := range image {
        reversedImage[rows-1-i] = row
    }
    return reversedImage
}

Scala

class Solution {
  def flipImage(image: Array[Array[Int]]): Array[Array[Int]] = {
    image.indices.map { i =>
      image(image.length - 1 - i)
    }.toArray
  }
}

Kotlin

class Solution {
  fun flipImage(image: Array<IntArray>): Array<IntArray> {
    return Array(image.size) { i -> image[image.size - 1 - i] }
  }
}

Swift

class Solution {
    func flipImage(_ image: [[Int]]) -> [[Int]] {
        return (0..<image.count).map { i in image[image.count - 1 - i] }
    }
}

Ruby

class Solution
  def flip_image(image)
    image.reverse
  end
end

Ruby's reverse method returns a new array with elements in reverse order.

Rust

pub fn flip_image(image: Vec<Vec<i32>>) -> Vec<Vec<i32>> {
    (0..image.len())
        .map(|i| image[image.len() - 1 - i].clone())
        .collect()
}

C#

using System.Linq;

public class Solution {
    public int[][] FlipImage(int[][] image) {
        return Enumerable
                 .Range(0, image.Length)
                 .Select(i => image[image.Length - 1 - i])
                 .ToArray();
    }
}

Dart

class Solution {
  List<List<int>> flipImage(List<List<int>> image) {
    return List.generate(image.length, (i) => image[image.length - 1 - i]);
  }
}

PHP

class Solution {
    public function flipImage(array $image): array {
        return array_reverse($image);
    }
}

Practice Makes Perfect

Once you've nailed flipping on the horizontal axis, try these related matrix problems to strengthen your 2D array skills:

  • Rotate an image by 90 degrees (combine transpose with flip)
  • Spiral order traversal of a matrix
  • Zero out rows and columns containing zeros
  • Transpose a matrix

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're just getting started or aiming for your dream role, mastering fundamentals like 2D array manipulation will set you up for success.

Frequently Asked Questions

What does flipping an image on its horizontal axis mean?
Flipping on the horizontal axis means reversing the order of rows, so the top row becomes the bottom row and vice versa. It is equivalent to turning the image upside down. Each row's internal contents remain unchanged; only the row order is reversed.
What is the time complexity of flipping a 2D array?
The time complexity is O(m * n) where m is the number of rows and n is the number of columns. Every element must be copied to the new result array, so the work scales linearly with the total number of pixels in the image.
What is the space complexity of flipping a 2D array?
The space complexity is O(m * n) because you create a new 2D array of the same dimensions to hold the flipped result. If the problem allowed in-place modification, you could swap rows with O(1) extra space, but this problem requires returning a copy.
What is the difference between flipping on the horizontal axis and the vertical axis?
Flipping on the horizontal axis reverses row order, turning the image upside down. Flipping on the vertical axis reverses each row internally, creating a mirror image. A horizontal flip swaps image[0] with image[n-1], while a vertical flip swaps image[i][0] with image[i][cols-1] within every row.
Can you flip a 2D array in-place?
Yes. You can swap the first row with the last, the second with the second-to-last, and so on. This requires only O(1) extra space since you just swap row references. However, this problem specifically asks you to return a new copy, so in-place modification is not appropriate here.
How does the index mapping formula work for flipping rows?
For an array with n rows, row i in the original maps to row n - 1 - i in the result. Row 0 maps to row n-1, row 1 maps to row n-2, and so on. This formula ensures the first row ends up last and the last row ends up first.
Why is this problem useful for coding interview preparation?
It tests your ability to work with 2D arrays and understand index manipulation, which are foundational skills. Many harder problems like matrix rotation, spiral traversal, and image processing build on these same concepts. It also introduces functional programming patterns like streams and map operations.
How do you handle edge cases like an empty image?
An empty image (zero rows) should return an empty array. A single-row image returns a copy of that single row since flipping one row has no visible effect. An image with empty rows (like [[]]) should return the same structure. Always check these cases in your solution.