88. Merge Sorted Array
Quick Identifier
- Topic: arrays-hashing
- Pattern: Two Pointers (Backward Sweep)
- Hint: If you merge from the front, you will overwrite the elements of
nums1before you evaluate them. Merge from the back, placing the largest elements in the empty0slots. - Difficulty: Easy
- Companies: Amazon, Facebook, Microsoft, Bloomberg, Apple
- LeetCode: 88
Quick Sights
- Time Complexity:
O(m + n). We iterate through both arrays exactly once from back to front. - Space Complexity:
O(1). The problem explicitly demands an in-place solution. We use the trailing zeros ofnums1as our buffer. - Core Mechanism: Maintain three pointers:
p1starting at the last actual element ofnums1(m - 1).p2starting at the last element ofnums2(n - 1).pstarting at the very end ofnums1(m + n - 1), marking the current write position. Comparenums1[p1]andnums2[p2]. Write the larger of the two tonums1[p]. Decrementpand the pointer of the array you just wrote from.
Concept Explanation
As a senior engineer, the leap in logic here is recognizing that empty buffer space at the end of an array is a massive hint to process backwards.
- The Overwrite Danger: If you start from index 0,
nums2[0]might be smaller thannums1[0]. If you writenums2[0]intonums1[0], you have permanently destroyed data you still need to evaluate. To do this from the front, you would need to allocate an entirely new $O(m+n)$ array. - The Safe Zone: Look at the end of
nums1. It contains $n$ zeros. This is your safe zone. Because both arrays are sorted in ascending order, the absolute largest elements must be at their respective tails (m-1andn-1). - The Backward Merge: Since you know the largest elements are at the tails, you can safely grab the largest element between
nums1andnums2and drop it into the absolute last slot ofnums1(m+n-1), which is safely filled with a useless0. - The Termination: You continue this process until
p2runs out of bounds (drops below 0). Ifp1runs out first, you still have elements innums2left to copy. Ifp2runs out first, you are done—the rest ofnums1is already perfectly sorted in place!
Diagram
This flowchart traces the three-pointer logic merging backwards into the buffer.
Loading diagram…
Study Pattern (SDE3+)
- 0–3 min: Immediately point out that the $O(1)$ space requirement + the trailing zeros explicitly dictates a backward traversal. Explain the overwrite danger of forward traversal.
- Implementation pass: The
whileloop condition only needs to bep2 >= 0. You do not need to checkp1 >= 0as the main loop condition because ifp1finishes first, you just copy the rest ofp2. Ifp2finishes first,p1is already sitting in its correct, sorted position! - 5 min extension: What if
nums1didn't have the buffer space at the end? You'd be forced into $O(n)$ space allocation. Alternatively, if this was a Linked List instead of an Array (LeetCode 21), you would merge forward because Linked Lists don't suffer from array overwrite problems (you just adjust pointers).
Approaches
- Brute force — Append
nums2to the end ofnums1(ignoring the zeros), thensort()the whole thing. Time: $O((m+n) \log(m+n))$, Space: $O(1)$ (or $O(m+n)$ depending on sort implementation). Suboptimal. - Forward Merge with Extra Array — Merge into a new array from the front, then copy back. Time: $O(m+n)$, Space: $O(m+n)$. Suboptimal.
- Backward Merge (Three Pointers) — Time: $O(m+n)$, Space: $O(1)$. (Always pick this)
Optimal Solution
Go
func merge(nums1 []int, m int, nums2 []int, n int) {
// Initialize pointers starting from the back
p1 := m - 1
p2 := n - 1
p := m + n - 1
// We only care about exhausting nums2.
// If nums1 exhausts first, we still need to copy the rest of nums2.
// If nums2 exhausts first, nums1 is already perfectly sorted in place.
for p2 >= 0 {
// If p1 is valid and points to a larger number, place it at p
if p1 >= 0 && nums1[p1] > nums2[p2] {
nums1[p] = nums1[p1]
p1--
} else {
// Otherwise, place nums2's number at p
nums1[p] = nums2[p2]
p2--
}
p--
}
}JavaScript
function merge(nums1, m, nums2, n) {
let p1 = m - 1;
let p2 = n - 1;
let p = m + n - 1;
while (p2 >= 0) {
if (p1 >= 0 && nums1[p1] > nums2[p2]) {
nums1[p] = nums1[p1];
p1--;
} else {
nums1[p] = nums2[p2];
p2--;
}
p--;
}
}Walkthrough
Input: nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
| p | p1 | p2 | nums1[p1] | nums2[p2] | Larger | Action | nums1 State |
|---|---|---|---|---|---|---|---|
| 5 | 2 | 2 | 3 | 6 | 6 (from p2) | Write 6, p2--, p-- | [1,2,3,0,0,6] |
| 4 | 2 | 1 | 3 | 5 | 5 (from p2) | Write 5, p2--, p-- | [1,2,3,0,5,6] |
| 3 | 2 | 0 | 3 | 2 | 3 (from p1) | Write 3, p1--, p-- | [1,2,3,3,5,6] |
| 2 | 1 | 0 | 2 | 2 | 2 (from p2) | Write 2, p2--, p-- | [1,2,2,3,5,6] |
Loop terminates because p2 becomes -1. (Notice nums1[0] and nums1[1] were never touched because they were already in the correct place!).
Edge Cases
n == 0(nums2is empty). The loop conditionp2 >= 0fails instantly,nums1is unchanged, which is correct.m == 0(nums1is technically empty, just0s buffer).p1starts at-1. Thep1 >= 0condition fails, so it safely copies all ofnums2directly intonums1.
Pitfalls
- Using
while (p1 >= 0 && p2 >= 0). If you do this, you must write a second loop afterward to catch any remaining elements innums2. (You don't need a catch loop fornums1because they are already innums1). The singlewhile (p2 >= 0)condition is cleaner. - Confusing
mandnas the physical array lengths.len(nums1)ism+n.mis just the logical count of valid elements.
Similar Problems
- 021. Merge Two Sorted Lists — The exact same problem, but applied to Linked Lists.
- 026. Remove Duplicates from Sorted Array — Two pointer overwrite logic.
- 977. Squares of a Sorted Array — Another problem that requires building the result array starting from the back.
Mind-Map Tags
#arrays #two-pointers #backward-sweep #in-place #merge
Mark this page when you finish learning it.
Last updated on
Spotted something unclear or wrong on this page?