Why does an in-place array reversal need only n/2 swaps rather than n?
answer
- count the positions one swap fixes
- two indices walking toward each other
- the loop stops where the pointers cross
- a pair swapped twice returns home
basics
~10 sEach swap places two elements at once, so n/2 swaps fix all n positions. Looping over every index instead swaps each pair twice, which undoes the reversal and hands back the original array.
solid answer
~40 sReversal is a two-pointer walk: `i` starts at 0, `j` at `n-1`, and while `i < j` you swap `a[i]` with `a[j]` and step both inward. One swap puts two elements in their final slots, so n/2 swaps cover n positions. The invariant is that `a[0..i-1]` and `a[j+1..n-1]` are already final while `a[i..j]` is untouched original order; the loop ends when the pointers cross. If instead you run `i` from 0 to `n-1` swapping `a[i]` with `a[n-1-i]`, every pair is swapped a second time when the mirror index comes around, and the array comes back unchanged. For odd n the middle position's mirror is itself, so `i < j` correctly leaves it alone. Cost: O(n) time, O(1) extra space.
code
pseudocode · 10 linesi = 0
j = length(a) - 1
while i < j
swap(a[i], a[j])
i = i + 1
j = j - 1
// invariant before each test of i < j:
// a[0..i-1] and a[j+1..n-1] hold final values
// a[i..j] is still in original ordergo deeper
Recall the two-pointer shape and the n/2 swap count, and be ready to say out loud why a loop over every index cancels itself and returns the input unchanged.
Explain the loop invariant — finished prefix and suffix outside, untouched original inside — and state the exact termination condition for both odd and even lengths.
Show that range reversal is the primitive other in-place rearrangement is built from, and note that the O(1) space claim holds only for the iterative form.
Own the framing that reversal is the cheapest sequential-access rearrangement available, and be able to say when even two full sequential passes over the data are too expensive to accept.
## What the operation is Reversing an array in place means rewriting the same storage so the element at index 0 ends at index n-1, index 1 ends at n-2, and so on, without allocating a second array of size n. The canonical shape is two indices walking toward each other: `i` at 0, `j` at `n-1`, swap while `i < j`, then step `i` forward and `j` backward. ## Why the swap count is n/2 A swap is a two-for-one operation. A single `swap(a[i], a[j])` puts `a[i]` into its final position *and* `a[j]` into its final position simultaneously. There are n positions to fix, each swap fixes two, so n/2 swaps (rounded down) fix all of them. The rounding matters: when n is odd, the middle index's mirror is itself, that element is already where it belongs, and no swap is spent on it. ## The invariant that makes it obvious Before each test of the loop condition: - `a[0..i-1]` and `a[j+1..n-1]` already hold their final, reversed values. - `a[i..j]` still holds the original elements in their original relative order. Each iteration extends the finished prefix and the finished suffix by one element each, and shrinks the untouched middle by two. Termination is guaranteed because the middle shrinks every iteration. When the loop exits, the middle has size 0 (even n) or size 1 (odd n), and a middle of size 1 is already correct by definition. ## The bug this question aims at The most common wrong answer is a loop over the whole range: ``` for i in 0..n-1 swap(a[i], a[n-1-i]) ``` This is symmetric: the pair (i, n-1-i) is swapped once when the counter reaches `i`, and swapped a second time when the counter reaches `n-1-i`. A pair swapped twice is back where it started, so the loop returns the *original* array (with an odd-length middle element pointlessly swapped with itself). The code reads plausibly, compiles, never throws, and silently produces wrong output — which is exactly why interviewers ask about the bound instead of asking you to define reversal. ## Costs, stated precisely Time is O(n): n/2 swaps, each constant-time for fixed-size elements. Writing "O(n/2)" is not meaningful notation — constant factors are dropped, so n/2 swaps is O(n). Extra space is O(1): two index variables plus the single temporary cell inside the swap. That temporary is reused every iteration and does not grow with n, so it does not break the in-place claim. O(1) here refers to *auxiliary* space; the array itself is storage you were given permission to modify. One caveat worth having ready: the same reversal written recursively — swap the ends, then recurse on `(i+1, j-1)` — is equally correct and still O(n) time, but its recursion depth is n/2 frames, and stack depth counts as space. Unless those frames are eliminated, the recursive form is O(n) space and no longer in place. Recursion depth is space is a rule that catches many candidates who have just finished claiming O(1). ## Range reversal is the real primitive In practice you write `reverse(a, lo, hi)` rather than a whole-array version, using `(hi - lo + 1)/2` swaps. Rotations, block moves, and word-order reversals are all assembled from two or three range reversals, so the sub-range form is the building block worth memorising. It must tolerate a degenerate empty range (`lo > hi`) without touching anything, because callers routinely pass boundaries that collapse at the edges of their input. ## Why the access pattern is friendly Two streams walking toward each other from both ends are close to the most hardware-friendly pattern there is: both are sequential, both are predictable, and prefetching handles them well. That matters later, when you compare reversal-based rotation against cleverer schemes that touch memory in a scattered order. It also shifts the cost model when elements are large records rather than small handles: then the byte cost of moving each element, not the index arithmetic, dominates.
- What happens to the middle element when the array has odd length?Nothing, and that is correct. The middle index is its own mirror, so it already sits in its final position. The condition `i < j` skips it; using `i <= j` would swap it with itself — harmless but a wasted operation, and a hint that the author has not reasoned about the crossing point.
- How does the loop change if you only need to reverse a sub-range a[lo..hi]?Seed the pointers at `lo` and `hi` instead of 0 and `n-1`; the rest is identical, costing `(hi - lo + 1)/2` swaps. Make it a no-op when `lo >= hi` so degenerate and empty ranges are safe, because callers building rotations and block moves pass those boundaries constantly.
- Does the O(1) extra-space claim survive if you write the reversal recursively?Not in general. Swap the ends and recurse inward and you use n/2 stack frames, and recursion depth is part of space complexity. Unless those frames are eliminated by the compiler, the recursive version is O(n) space and is no longer in place — so claim O(1) only for the iterative form.
Two people at opposite ends of a row of chairs trade seats, then each steps one chair inward. They meet in the middle having moved everyone exactly once.
saying these in an interview costs you the question
- Loops i from 0 to n-1 swapping a[i] with a[n-1-i]
- Says reversal needs a second array of the same size
- Swaps the middle element of an odd-length array with itself
- Calls the cost O(n/2) instead of O(n)
- Claims the swap temporary breaks the O(1) space bound