Merge Sorted Array (In-Place)¶
Question¶
Given two sorted integer arrays \(nums1\) and \(nums2\), merge \(nums2\) into \(nums1\) as one sorted array.
- \(nums1\) has a length of \(m + n\).
- The first \(m\) elements are the actual sorted data.
- \(nums2\) has a length of \(n\).
- The merge must happen in-place within \(nums1\).
Solution¶
Pattern¶
Backwards Two-Pointer Instead of starting from the beginning and shifting elements, we compare elements from the ends of the sorted portions and fill the target array from the largest to the smallest.
How to Identify¶
- Input arrays are already sorted.
- Requirement for In-Place modification.
- One array has trailing "empty" space (usually represented as 0s) to accommodate the other.
Description¶
Step-by-step explanation:
- Initialize three pointers:
p1at index \(m-1\) (end of actual data innums1).p2at index \(n-1\) (end ofnums2).pMergedat index \(m+n-1\) (the very end of the \(nums1\) array).
- Compare \(nums1[p1]\) and \(nums2[p2]\).
- Place the larger of the two at \(nums1[pMerged]\) and decrement the respective pointers.
- Continue until \(p2 < 0\).
- Crucial Choice: We do not need a cleanup loop for \(p1\). If \(p2\) finishes first, the remaining elements in \(nums1\) are already sorted and in their final positions.
The Intuition¶
Think of this as Reverse Competitive Filling. If you started from the front, you would have to "push" existing elements in \(nums1\) out of the way to make room for \(nums2\), which is expensive (\(O(m \cdot n)\)). By starting from the back, where we know there is "useless" empty space, we fill the largest values first. It’s like filling a shelf from right to left because the left side is already occupied by books you don't want to drop.
Complexity¶
| Label | Worst | Average |
|---|---|---|
| Time Complexity | \(O(m + n)\) | \(O(m + n)\) |
| Space Complexity | \(O(1)\) | \(O(1)\) |
Time Complexity¶
We iterate through both arrays exactly once. In the worst case (all elements of \(nums2\) are smaller than \(nums1\)), we perform \(m+n\) operations.
Space Complexity¶
The modification is done in-place, using only three integer pointers regardless of the input size.
Code¶
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
// Pointer for the end of the sorted data in nums1
int p1 = m - 1;
// Pointer for the end of nums2
int p2 = n - 1;
// Pointer for the end of the total nums1 capacity
int pMerged = m + n - 1;
// While there are elements to process in nums2
while (p2 >= 0) {
// If nums1 still has elements and its current element is larger
if (p1 >= 0 && nums1[p1] > nums2[p2]) {
nums1[pMerged--] = nums1[p1--];
} else {
// Otherwise, nums2's element is larger or p1 is exhausted
nums1[pMerged--] = nums2[p2--];
}
}
// No need to copy nums1's remaining elements; they are already in place.
}
}
Caveats¶
- Empty nums1: If \(m=0\), the logic correctly falls into the
elseblock and copies all of \(nums2\). - Empty nums2: If \(n=0\), the
while (p2 >= 0)loop never executes, and \(nums1\) remains unchanged (correct). - Overwriting: This approach only works because the extra space is at the end of the array. If it were at the beginning, we would merge from front to back.
Concepts to Think About¶
- Stability: Is this merge stable? (Yes, if we use
>=correctly, though usually not a requirement for primitives). - Memory Management: In-place algorithms are vital in embedded systems or high-frequency trading where memory allocation is a bottleneck.
- Two-Pointer Variations: This is a "Three-Pointer" variation, but the logic follows the standard Two-Pointer merge pattern found in Merge Sort.
- Sentinel Values: Could we use sentinels? Theoretically, but it's less efficient than pointer bounds checks.
Logical Follow-up¶
Question: What if nums1 did NOT have extra space at the end? Solution: We would be forced to use \(O(m)\) extra space to store a copy of \(nums1\) or use the Gap Method (based on Shell Sort) to achieve \(O(1)\) space with \(O((n+m) \log(n+m))\) time.
Question: What if the arrays were too large to fit in memory? Solution: We would use External Merge Sort, reading chunks of each array, merging them, and writing them to a new external storage file.