Home DSA Rotate Array Problem Explained — 3 Approaches, Real Trade-offs

Rotate Array Problem Explained — 3 Approaches, Real Trade-offs

In Plain English 🔥
Imagine a queue of 5 people waiting for coffee. The barista says 'everyone shift two spots to the right' — the last two people wrap around and stand at the front. That's array rotation: you're not changing WHO is in the line, just WHERE they stand. The tricky part is doing this efficiently without making a copy of the entire line.
⚡ Quick Answer
Imagine a queue of 5 people waiting for coffee. The barista says 'everyone shift two spots to the right' — the last two people wrap around and stand at the front. That's array rotation: you're not changing WHO is in the line, just WHERE they stand. The tricky part is doing this efficiently without making a copy of the entire line.

Array rotation shows up more often than you'd think in real engineering. Circular buffers in operating systems, job scheduling queues, and even image processing pipelines all rely on the idea that a sequence of elements can 'wrap around' efficiently. When a system rotates a task queue by one slot every tick, it can't afford to allocate a new array each time — that would tank performance under load. Getting rotation right is the difference between O(1) extra space and an unnecessary memory hit.

The problem itself sounds deceptively simple: given an array of n elements, shift every element k positions to the right, wrapping the tail back around to the front. But the moment you ask 'how do I do this without extra memory?', the problem gets genuinely interesting. Naive solutions work, but they hide costs that only surface at scale — and that's exactly what interviewers are probing for when they ask this question.

By the end of this article you'll understand three distinct approaches to array rotation, the exact time and space cost of each, and — most importantly — WHY the in-place reversal trick works (not just that it does). You'll be able to pick the right tool for the context, defend your choice under interview pressure, and avoid the two gotchas that trip up even experienced developers.

Brute Force: Rotate One Step at a Time, k Times

The most natural instinct is to rotate the array one position at a time, repeating that k times. For a single right rotation, you save the last element, shift every other element one spot to the right, then place the saved element at index 0. It's easy to reason about because you can watch it happen step by step.

The cost, though, is brutal. For each of the k rotations you're touching every n elements, giving you O(n × k) time. On a 10-element array rotated 3 times that's 30 operations — fine. On a 100,000-element array rotated 50,000 times, that's 5 billion operations. This approach is only acceptable when you know k is tiny and n is small.

There's also a subtle correctness trap here: what if k is larger than n? Rotating a 5-element array by 7 is the same as rotating it by 2 (because after 5 rotations you're back where you started). Always reduce k with k = k % n before you start, or you'll do redundant full loops.

BruteForceRotation.java · JAVA
1234567891011121314151617181920212223242526272829303132333435363738394041424344
public class BruteForceRotation {

    /**
     * Rotates the array to the right by 'steps' positions.
     * Strategy: perform a single-step rotation exactly 'steps' times.
     * Time:  O(n * steps)  — we touch every element once per step
     * Space: O(1)          — only one extra variable to hold the displaced element
     */
    public static void rotateRight(int[] elements, int steps) {
        int length = elements.length;
        if (length == 0) return;

        // Reduce redundant full-cycle rotations.
        // e.g. rotating 5 elements by 7 is identical to rotating by 2.
        steps = steps % length;

        for (int round = 0; round < steps; round++) {
            // Save the element that will be displaced from the end
            int displaced = elements[length - 1];

            // Shift every element one position to the right
            for (int i = length - 1; i > 0; i--) {
                elements[i] = elements[i - 1]; // each element takes the value of its left neighbour
            }

            // The displaced tail element wraps around to the front
            elements[0] = displaced;
        }
    }

    public static void main(String[] args) {
        int[] schedule = {1, 2, 3, 4, 5, 6, 7};
        System.out.println("Before rotation: " + java.util.Arrays.toString(schedule));

        rotateRight(schedule, 3);
        System.out.println("After rotating right by 3: " + java.util.Arrays.toString(schedule));

        // Edge case: k larger than array length
        int[] taskQueue = {10, 20, 30, 40, 50};
        System.out.println("\nBefore rotation: " + java.util.Arrays.toString(taskQueue));
        rotateRight(taskQueue, 7); // 7 % 5 = 2, so effectively 2 steps
        System.out.println("After rotating right by 7 (same as 2): " + java.util.Arrays.toString(taskQueue));
    }
}
▶ Output
Before rotation: [1, 2, 3, 4, 5, 6, 7]
After rotating right by 3: [5, 6, 7, 1, 2, 3, 4]

Before rotation: [10, 20, 30, 40, 50]
After rotating right by 7 (same as 2): [40, 50, 10, 20, 30]
⚠️
Watch Out: Skipping the Modulo StepIf you forget k = k % n and k equals n exactly, you run the full inner loop n times and end up with the original array — correct result, wasted work. If k > n you've done multiple unnecessary full cycles. Always reduce k before the outer loop. This one line separates a beginner's solution from a professional one.

Extra Array Approach: Trade Memory for Clarity

The extra-array approach is the 'obvious' O(n) time solution. You allocate a second array of the same size, calculate each element's destination index with the formula (i + k) % n, copy everything into the right spot, then copy it back. It's clean, readable, and easy to verify by hand.

The destination formula is worth understanding properly. When you rotate right by k, the element currently at index i should land at index (i + k) % n. The modulo handles the wrap-around automatically — no special case needed for elements near the tail.

The trade-off is O(n) extra space. In most application code that's completely acceptable — a few kilobytes for a scheduling array won't move the needle. But in memory-constrained environments (embedded systems, high-frequency trading with tight heap budgets, or the constraints of a LeetCode problem that says 'O(1) space'), this is disqualifying. Knowing when the extra allocation is fine versus when it's a problem is itself a sign of engineering maturity.

ExtraArrayRotation.java · JAVA
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
public class ExtraArrayRotation {

    /**
     * Rotates the array to the right by 'steps' positions using a temporary buffer.
     * Time:  O(n) — single pass to place elements, single pass to copy back
     * Space: O(n) — we allocate a second array of equal size
     */
    public static void rotateRight(int[] elements, int steps) {
        int length = elements.length;
        if (length == 0) return;

        steps = steps % length; // guard against k >= n
        if (steps == 0) return;  // guard against no-op rotation

        int[] buffer = new int[length];

        // Place each element at its final destination in the buffer.
        // An element at index i in a right-rotation moves to (i + steps) % length.
        for (int i = 0; i < length; i++) {
            int destination = (i + steps) % length;
            buffer[destination] = elements[i];
        }

        // Copy the correctly-ordered buffer back into the original array
        // so the caller sees the result in-place.
        System.arraycopy(buffer, 0, elements, 0, length);
    }

    public static void main(String[] args) {
        int[] playlist = {"Song A", "Song B", "Song C", "Song D", "Song E"}; // conceptual
        int[] trackIds = {101, 102, 103, 104, 105};

        System.out.println("Playlist before shuffle-rotate: " + java.util.Arrays.toString(trackIds));
        rotateRight(trackIds, 2);
        System.out.println("Playlist after rotating right by 2: " + java.util.Arrays.toString(trackIds));

        // Verify the destination formula manually:
        // index 0 (101) -> (0+2)%5 = 2  ✓
        // index 1 (102) -> (1+2)%5 = 3  ✓
        // index 2 (103) -> (2+2)%5 = 4  ✓
        // index 3 (104) -> (3+2)%5 = 0  ✓ (wraps around)
        // index 4 (105) -> (4+2)%5 = 1  ✓ (wraps around)

        int[] sensors = {5, 10, 15, 20, 25, 30};
        System.out.println("\nSensor readings before: " + java.util.Arrays.toString(sensors));
        rotateRight(sensors, 6); // 6 % 6 = 0, no change expected
        System.out.println("After rotating by 6 (full cycle): " + java.util.Arrays.toString(sensors));
    }
}
▶ Output
Playlist before shuffle-rotate: [101, 102, 103, 104, 105]
Playlist after rotating right by 2: [104, 105, 101, 102, 103]

Sensor readings before: [5, 10, 15, 20, 25, 30]
After rotating by 6 (full cycle): [5, 10, 15, 20, 25, 30]
⚠️
Pro Tip: The Formula is ReusableThe destination formula (i + k) % n works for left rotation too — just change it to (i - k + n) % n. The + n prevents negative indices in languages where modulo of a negative number stays negative (Java included). Burn this into memory: it appears in circular buffer implementations, round-robin schedulers, and Caesar cipher encoding.

The Reversal Trick: O(n) Time, O(1) Space — and Why It Works

This is the approach that separates people who've thought deeply about arrays from those who haven't. It uses three in-place reversals to achieve a full rotation without any extra memory. It feels like magic the first time you see it, but the logic is completely provable.

Here's the insight. A right rotation by k means the last k elements move to the front, and the first n-k elements shift to the back. Think of the array in two chunks: the 'tail' (last k elements) and the 'head' (first n-k elements). If you reverse the entire array, then reverse each chunk independently, you get the rotated result.

Why? Reversing the whole array puts the tail at the front and the head at the back — but both in reversed order. Reversing each section independently corrects the internal order within each chunk. The two reversal errors cancel each other out. It's like turning a sock inside-out twice to clean it: two inversions restore the original orientation, but in a new overall position.

This is the solution LeetCode expects for their 'Rotate Array' problem (problem 189) when they ask for O(1) extra space, and it's the answer that lands job offers.

ReversalTrickRotation.java · JAVA
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
public class ReversalTrickRotation {

    /**
     * Rotates the array to the right by 'steps' positions using three reversals.
     * Time:  O(n) — each element is touched at most twice across all three reversals
     * Space: O(1) — swaps happen in-place with a single temporary variable
     *
     * The three-step recipe:
     *   1. Reverse the ENTIRE array
     *   2. Reverse the FIRST k elements  (the future front section)
     *   3. Reverse the REMAINING n-k elements (the future back section)
     */
    public static void rotateRight(int[] elements, int steps) {
        int length = elements.length;
        if (length == 0) return;

        steps = steps % length;
        if (steps == 0) return;

        // Step 1: Flip the whole array
        // [1,2,3,4,5,6,7] with k=3 -> [7,6,5,4,3,2,1]
        reverse(elements, 0, length - 1);

        // Step 2: Reverse the first 'steps' elements
        // [7,6,5,4,3,2,1] -> [5,6,7,4,3,2,1]
        reverse(elements, 0, steps - 1);

        // Step 3: Reverse the remaining elements from index 'steps' to end
        // [5,6,7,4,3,2,1] -> [5,6,7,1,2,3,4]
        reverse(elements, steps, length - 1);
    }

    /**
     * Reverses a sub-section of the array between indices 'from' and 'to' inclusive.
     * Uses two-pointer swap — no extra array needed.
     */
    private static void reverse(int[] elements, int from, int to) {
        while (from < to) {
            int temp = elements[from];
            elements[from] = elements[to];
            elements[to] = temp;
            from++;
            to--;
        }
    }

    public static void main(String[] args) {
        // --- Example 1: Standard rotation ---
        int[] taskQueue = {1, 2, 3, 4, 5, 6, 7};
        System.out.println("Original task queue:  " + java.util.Arrays.toString(taskQueue));
        rotateRight(taskQueue, 3);
        System.out.println("After right rotate k=3: " + java.util.Arrays.toString(taskQueue));

        // --- Example 2: k = 1 (shift by one, common in round-robin) ---
        int[] servers = {101, 102, 103, 104, 105};
        System.out.println("\nServer order before:  " + java.util.Arrays.toString(servers));
        rotateRight(servers, 1);
        System.out.println("After round-robin shift: " + java.util.Arrays.toString(servers));

        // --- Example 3: k > n (handled by modulo) ---
        int[] pixels = {255, 128, 64, 32, 16};
        System.out.println("\nPixel values before:  " + java.util.Arrays.toString(pixels));
        rotateRight(pixels, 12); // 12 % 5 = 2
        System.out.println("After rotate k=12 (effective k=2): " + java.util.Arrays.toString(pixels));
    }
}
▶ Output
Original task queue: [1, 2, 3, 4, 5, 6, 7]
After right rotate k=3: [5, 6, 7, 1, 2, 3, 4]

Server order before: [101, 102, 103, 104, 105]
After round-robin shift: [105, 101, 102, 103, 104]

Pixel values before: [255, 128, 64, 32, 16]
After rotate k=12 (effective k=2): [32, 16, 255, 128, 64]
🔥
Interview Gold: Prove It with a Tiny ExampleWhen an interviewer asks you to justify the reversal trick, don't just say 'it works'. Trace [1,2,3,4,5] with k=2 on the whiteboard: full reverse gives [5,4,3,2,1], reverse first 2 gives [4,5,3,2,1], reverse last 3 gives [4,5,1,2,3]. That's correct. Showing the three intermediate steps proves you understand WHY, not just WHAT — and that's what gets you the offer.
AspectBrute Force (One-by-One)Extra Array (Buffer)Reversal Trick (In-Place)
Time ComplexityO(n × k)O(n)O(n)
Space ComplexityO(1)O(n)O(1)
Code ReadabilityVery easy to followEasy to followRequires explanation
Handles k > n?Only with modulo guardOnly with modulo guardOnly with modulo guard
Best Use CaseTiny arrays, tiny kWhen memory isn't a constraintProduction / interview code
Mutation of input?Yes (in-place)Yes (copies buffer back)Yes (in-place)
Risk of TLE on large n?High when k is largeNoneNone
LeetCode 189 optimal?No — TLE on large inputsPartially (fails O(1) space)Yes — accepted optimal

🎯 Key Takeaways

  • Always reduce k with k % n before rotating — skipping this turns an O(n) algorithm into O(n²) or causes silent redundant full cycles.
  • The reversal trick works because two inversions cancel out: reversing the whole array then reversing each segment independently restores internal order in a new overall position.
  • The destination-index formula (i + k) % n is reusable across circular buffers, Caesar ciphers, and round-robin schedulers — memorise it, not just for rotations.
  • Choose your approach based on constraints: brute force for throwaway scripts with tiny data, extra array when memory is free and clarity matters, reversal trick for production systems and any interview that mentions O(1) space.

⚠ Common Mistakes to Avoid

  • Mistake 1: Not reducing k with modulo before rotating — If k equals n, you do a full cycle of n×n operations for free and return the original array. If k > n you silently do extra useless work. The symptom is mysteriously slow performance on large inputs that pass correctness tests. Fix: always write steps = steps % length as the first real line of your function, before any loop or reversal.
  • Mistake 2: Off-by-one in the reversal boundaries — A common error is writing reverse(elements, 0, steps) instead of reverse(elements, 0, steps - 1). Since Java arrays are zero-indexed and both endpoints are inclusive in the typical two-pointer reverse, including index 'steps' bleeds into the second segment and corrupts the result. The symptom is a result that looks almost right but has two adjacent elements in the wrong order. Fix: double-check your boundary arguments — the first segment is [0, steps-1], the second is [steps, length-1].
  • Mistake 3: Confusing left rotation and right rotation — Right rotation by k moves elements toward higher indices, wrapping the tail to the front. Left rotation by k does the opposite. The mistake usually comes from misreading the problem statement and then being confused when the output looks like a mirror of what's expected. Fix: test with [1,2,3,4,5] and k=1. Right rotation should give [5,1,2,3,4]. Left rotation should give [2,3,4,5,1]. Lock this into muscle memory and always verify with a single-step test case before running your full solution.

Interview Questions on This Topic

  • QCan you rotate an array in-place using O(1) extra space? Walk me through your approach and prove it's correct with a small example.
  • QWhat happens when k is greater than or equal to the array length? How does your solution handle that edge case, and why is the modulo operation the right fix?
  • QIf I asked you to rotate left instead of right, how would you adapt your reversal-trick solution? What changes in the boundary indices, and can you derive the formula for the left-rotation destination index?

Frequently Asked Questions

What is the most efficient way to rotate an array in Java?

The reversal trick achieves O(n) time and O(1) space — the theoretical optimum. It performs three in-place sub-array reversals: reverse the full array, reverse the first k elements, then reverse the remaining n-k elements. No extra array is allocated, and every element is touched at most twice.

What is the difference between left rotation and right rotation of an array?

Right rotation by k moves elements toward higher indices, wrapping the last k elements around to the front. Left rotation by k does the opposite — the first k elements wrap to the back. For a right rotation the destination formula is (i + k) % n; for left rotation it's (i - k + n) % n. The +n in the left-rotation formula prevents negative indices.

Why does rotating an array by k give the same result as rotating by k % n?

After exactly n rotations (where n is the array length), every element has returned to its original position — you've done a full cycle. Any additional rotations beyond a full cycle just repeat positions you've already visited. So rotating by 7 on a 5-element array is identical to rotating by 7 % 5 = 2. Applying the modulo before your algorithm is a correctness and performance guard in one.

🔥
TheCodeForge Editorial Team Verified Author

Written and reviewed by senior developers with real-world experience across enterprise, startup and open-source projects. Every article on TheCodeForge is written to be clear, accurate and genuinely useful — not just SEO filler.

← PreviousMatrix Traversal PatternsNext →Merge Intervals Problem
Forged with 🔥 at TheCodeForge.io — Where Developers Are Forged