Merge Two Sorted Arrays Without Extra Space is a classic twist on the everyday merge step. The merge itself is easy with a scratch array — the challenge is doing it in place, using only O(1) extra memory. The trick is the gap algorithm, a Shell-sort-flavored merge.
Problem. Given two sorted arrays a (length n) and b (length m), rearrange their elements so
that, read as one sequence a then b, everything is in non-decreasing order. You may not allocate
a third array — only O(1) extra space is allowed.
Example: a = [1, 4, 7, 8, 10], b = [2, 3, 9] → afterwards a = [1, 2, 3, 4, 7], b = [8, 9, 10].
The slow way first
The textbook merge walks both arrays with two pointers and writes the smaller value into a new array of size n + m. That is clean and O(n + m) time, but it spends O(n + m) extra space — exactly what this problem forbids.
You could instead, for each element of b, insert it into its correct slot in a by shifting elements over (insertion-sort style). That uses no extra array, but the shifting makes it O(n · m) in the worst case — too slow.
The question to ask: can I fix the order in place without shifting one element at a time? Yes — by comparing elements that are far apart first, and closing that distance gradually.
The idea: compare across a shrinking gap
Treat a and b as one long virtual array of length n + m: positions 0 … n-1 live in a, positions n … n+m-1 live in b. Now run a single rule:
Pick a gap, starting at ceil((n + m) / 2). Sweep i from the left and compare the element at i with the one at i + gap. If the left one is bigger, swap them. After one full sweep, halve the gap (rounding up) and sweep again. Stop once the gap drops below 1.
Because both halves start sorted, comparing across a wide gap moves small values left and large values right in big jumps. Each smaller gap cleans up shorter-range disorder, and the final gap = 1 pass is just an adjacent sweep that guarantees full order.
Walk through it
Step through the animation. The two cells i and i + gap light up each comparison; out-of-order pairs swap and slide past each other. The gap label up top starts at 4, then shrinks to 2, then 1. By the time the gap = 1 sweep finishes, the joined view [1, 2, 3, 4, 7, 8, 9, 10] is fully sorted, split back into a and b.
Pseudocode
total = n + m
gap = ceil(total / 2)
while gap > 0:
for i from 0 while i + gap < total:
if element[i] > element[i + gap]:
swap element[i] and element[i + gap] # index into a or b as needed
if gap == 1:
gap = 0
else:
gap = ceil(gap / 2)The Python solution
def merge(a, b):
n, m = len(a), len(b)
total = n + m
gap = (total + 1) // 2
def at(k):
return a[k] if k < n else b[k - n]
def put(k, val):
if k < n: a[k] = val
else: b[k - n] = val
while gap > 0:
for i in range(total - gap):
if at(i) > at(i + gap):
lo, hi = at(i + gap), at(i)
put(i, lo); put(i + gap, hi)
gap = 0 if gap == 1 else (gap + 1) // 2
return a, btotal = n + mis the length of the virtual concatenated array.gap = (total + 1) // 2isceil(total / 2)written with integer math.at(k)andput(k, val)are the bridge: indexkreads or writesawhenk < n, otherwise it lands inbatk - n. This is what lets us treat the two arrays as one without copying.- The
for i in range(total - gap)sweep compares every pair exactlygapapart; theifswaps when the left value is bigger. gap = 0 if gap == 1 else (gap + 1) // 2halves the gap (rounding up) and ends the loop after the gap = 1 pass.
Complexity
| Case | Time | Notes |
|---|---|---|
| Extra array merge | O(n + m) (moderate) | but needs O(n + m) space |
| Insertion / shifting | O(n · m) (moderate) | in place but slow |
| Gap method (this solution) | O((n + m) log(n + m)) (moderate) | log rounds, each a linear sweep |
O(1) (fast)The gap halves each round, so there are about log(n + m) rounds, and each round is a single linear sweep. That gives O((n + m) log(n + m)) time with only O(1) extra space — the whole point of the problem.
When this pattern shows up
Whenever an interviewer adds the words without extra space or in place to a merge, sort, or rearrange problem, think about whether you can compare and swap elements a fixed distance apart and shrink that distance. The gap idea is the same engine behind Shell sort, and it is the standard answer to in-place merge.
The gap must round up when you halve it. If you let gap drop to 0 before doing a gap = 1 pass, you
skip the adjacent-element sweep and the result can be left unsorted. The clean guard is: from gap = 1,
set gap to 0; otherwise set it to (gap + 1) // 2.
Practice
With a = [1, 4, 7, 8, 10] and b = [2, 3, 9], the first gap is 4. When i = 1 we compare position 1 (value 4) with position 5 (value 2). What happens?
1. What is the starting gap for two arrays of total length 8?
2. Why can we treat the two arrays as one long array?
3. What is the extra space used by the gap method?
4. Why must the gap round up when it is halved?