Merge Sorted Array DSA: Brute Force, Merge Copy, and the Three-Pointer Trick
-
Last Updated: July 20, 2026
-
By: javahandson
-
Series
Learn Java in a easy way
Learn Merge Sorted Array DSA the beginner-friendly way. Compare brute force, merge copy, and the in-place three-pointer trick with dry runs and clean code.
Merge Sorted Array DSA is one of those problems that looks easy, then trips you up on the details. You get two sorted arrays, and you have to blend them into one sorted array. The twist is where the answer must live, and that small rule changes everything.
Here is the task in plain words. You get two arrays that are already sorted. The first one has extra empty space at the end, just enough to hold the second array. Your job is to pour the second array into the first, and keep the whole thing sorted.
In this guide we go slow. First we try the easy way, which is to dump and sort. Then we copy into a fresh array like a careful librarian. Finally we reach the clever three-pointer trick that fills the array from the back with no extra space. We dry run each idea by hand, so you can watch it click.
Let us pin down the rules first. This problem hides a few traps, and a clear statement keeps you safe.
Take nums1 = [1, 2, 3, 0, 0, 0] with m = 3, and nums2 = [2, 5, 6] with n = 3. After merging, nums1 should be [1, 2, 2, 3, 5, 6]. Those trailing zeros are not real data. They are just room to grow, so do not treat them as values to sort.
| 💡 Interview Insight Interviewers love the in-place twist here. Say you would build a brand new array, and they will nudge you toward the empty tail of nums1 instead. Naming that free space early shows you actually read the constraints. |
Before the code, let us name the ideas that carry this problem. Each one is small, and you will reuse them in many array questions.
Both arrays are already sorted. That is a gift. When two lists are sorted, you can walk them side by side and always know which next value is smaller. So you never need a full re-sort if you are careful.
The empty room sits at the end of nums1. So the safest place to write is the back, not the front. If you write from the front, you might stomp on a value you still need to read. Filling from the back avoids that clash. That single insight is the heart of the best solution.
The first idea most people reach for is blunt but honest. Copy every value from nums2 into the empty tail of nums1. Then sort the whole array and call it done.
for j from 0 to n-1:
nums1[m + j] = nums2[j] // fill the empty tail
sort(nums1) // let the library sort itLet us read that pseudocode one line at a time, in plain words.
So the plan is simple. First we pour nums2 into the empty slots. Then we let the sort clean up. It is short, and it is hard to get wrong.
import java.util.Arrays;
public class MergeSortedArrayBrute {
public static void merge(int[] nums1, int m,
int[] nums2, int n) {
for (int j = 0; j < n; j++) {
nums1[m + j] = nums2[j];
}
Arrays.sort(nums1);
}
public static void main(String[] args) {
int[] nums1 = { 1, 2, 3, 0, 0, 0 };
int[] nums2 = { 2, 5, 6 };
merge(nums1, 3, nums2, 3);
System.out.println(Arrays.toString(nums1));
// [1, 2, 2, 3, 5, 6]
}
}Let us read the code slowly. Each line does one small job.
This version is easy to read, and it always gives the correct answer. That is its strength. The weakness is that it throws away the fact that both inputs were already sorted.
Let us trace it with nums1 = [1, 2, 3, 0, 0, 0], m = 3, and nums2 = [2, 5, 6], n = 3. The empty slots are at indexes 3, 4, and 5. We copy first, then we sort. Read each row as one turn of the loop.
| Step | Action | nums1 after step |
|---|---|---|
| 1 | j = 0: copy nums2[0] = 2 into slot m + 0 = 3 | [1, 2, 3, 2, 0, 0] |
| 2 | j = 1: copy nums2[1] = 5 into slot m + 1 = 4 | [1, 2, 3, 2, 5, 0] |
| 3 | j = 2: copy nums2[2] = 6 into slot m + 2 = 5 | [1, 2, 3, 2, 5, 6] |
| 4 | loop ends, Arrays.sort reorders it all | [1, 2, 2, 3, 5, 6] |
Let us read the trace slowly, so nothing feels like magic.
So the stray 2 is the whole reason we need the sort. The copy alone does not keep things ordered. That sort works, but it redoes effort, because both inputs were already sorted before we started.
The copy loop takes O(n) time. The sort takes O((m + n) log (m + n)) time, which dominates. So the whole thing is O((m + n) log (m + n)). The space is O(1) beyond the array itself, since we sort in place. For small arrays this is fine. On big inputs, the extra log factor is wasted effort.
Here is a smarter middle ground. Both arrays are sorted, so let us use that. We keep a copy of the first m values. Then we merge that copy with nums2, writing results into nums1 from the front.
copy = first m values of nums1
i = 0 // pointer in copy
j = 0 // pointer in nums2
k = 0 // write pointer in nums1
while i < m and j < n:
if copy[i] <= nums2[j]:
nums1[k++] = copy[i++]
else:
nums1[k++] = nums2[j++]
copy any leftover from copy or nums2This one has more moving parts, so let us unpack each line.
The key idea: since both lists are already sorted, the smaller front value is always the next one to place. So one clean sweep is enough.
import java.util.Arrays;
public class MergeSortedArrayCopy {
public static void merge(int[] nums1, int m,
int[] nums2, int n) {
int[] copy = Arrays.copyOf(nums1, m);
int i = 0, j = 0, k = 0;
while (i < m && j < n) {
if (copy[i] <= nums2[j]) {
nums1[k++] = copy[i++];
} else {
nums1[k++] = nums2[j++];
}
}
while (i < m) nums1[k++] = copy[i++];
while (j < n) nums1[k++] = nums2[j++];
}
public static void main(String[] args) {
int[] nums1 = { 1, 2, 3, 0, 0, 0 };
int[] nums2 = { 2, 5, 6 };
merge(nums1, 3, nums2, 3);
System.out.println(Arrays.toString(nums1));
// [1, 2, 2, 3, 5, 6]
}
}Now let us map each line of Java to what it does.
This finishes in one clean sweep, so the time is O(m + n). That beats the brute force sort. The catch is the extra copy array, which costs O(m) space.
Let us trace with copy = [1, 2, 3] and nums2 = [2, 5, 6]. All three pointers start at 0. We write into nums1 from the front. The last column shows the part we have merged so far. An underscore means we have not written that slot yet.
| Step | copy[i] vs nums2[j] | Smaller wins | Merged so far |
|---|---|---|---|
| 1 | copy[0]=1 vs nums2[0]=2 | 1 wins, write 1, i and k step | [1, _, _, _, _, _] |
| 2 | copy[1]=2 vs nums2[0]=2 | tie, take copy, write 2, i and k step | [1, 2, _, _, _, _] |
| 3 | copy[2]=3 vs nums2[0]=2 | 2 wins, write 2, j and k step | [1, 2, 2, _, _, _] |
| 4 | copy[2]=3 vs nums2[1]=5 | 3 wins, write 3, i and k step | [1, 2, 2, 3, _, _] |
| 5 | copy empty, drain nums2 | write 5, then 6 | [1, 2, 2, 3, 5, 6] |
Let us narrate the pointer dance, step by step.
Once one side empties, no more comparing is needed. Whatever is left is already sorted, so we just copy it across.
Now for the version interviewers really want. The idea is to fill nums1 from the back, using its own empty tail as the workspace. No copy array, no re-sort, just one smart pass.
Think of it like stacking boxes into a truck from the rear. You place the biggest box last, right at the very back, then work forward. Because the empty space sits at the end of nums1, the back is always safe to write.
i = m - 1 // last real value in nums1
j = n - 1 // last value in nums2
k = m + n - 1 // last slot in nums1
while j >= 0:
if i >= 0 and nums1[i] > nums2[j]:
nums1[k--] = nums1[i--]
else:
nums1[k--] = nums2[j--]This is the clever one, so let us read every line carefully.
The trick is direction. We place the biggest remaining value at the back first. Then we work our way toward the front. The free space sits at the back of nums1. So slot k never lands on a value we still need to read.
public class MergeSortedArrayThreePointer {
public static void merge(int[] nums1, int m,
int[] nums2, int n) {
int i = m - 1;
int j = n - 1;
int k = m + n - 1;
while (j >= 0) {
if (i >= 0 && nums1[i] > nums2[j]) {
nums1[k--] = nums1[i--];
} else {
nums1[k--] = nums2[j--];
}
}
}
public static void main(String[] args) {
int[] nums1 = { 1, 2, 3, 0, 0, 0 };
int[] nums2 = { 2, 5, 6 };
merge(nums1, 3, nums2, 3);
System.out.println(java.util.Arrays.toString(nums1));
// [1, 2, 2, 3, 5, 6]
}
}The Java mirrors the pseudocode closely. Here is what each part does in the running code.
Why watch j and not k? Once nums2 is empty, the values still in nums1 are already sorted. They are also already sitting in the correct front slots. So there is nothing left to do, and we stop early.
And why the i >= 0 guard? Sometimes nums1 runs out of real values first. Then i drops below zero. The guard makes the check fail. So we fall into the else branch instead. From there we keep pouring the rest of nums2 into the front slots. That one small guard handles the tricky edge cases for free.
| 💡 Interview Insight A classic follow-up asks why we loop on j and not on k. The reason is neat: if nums2 empties before nums1, every remaining nums1 value is already sitting in its correct spot. So we can stop early and skip pointless writes. |
Watching the code run by hand makes it stick. Let us trace nums1 = [1, 2, 3, 0, 0, 0] with m = 3, and nums2 = [2, 5, 6] with n = 3. We start with i = 2, j = 2, and k = 5. Read each row as one turn of the loop, and watch the three pointers slide left.
| Step | i, j, k | nums1[i] vs nums2[j] | nums1 after step |
|---|---|---|---|
| 1 | i=2 j=2 k=5 | nums1[2]=3 vs nums2[2]=6 → 6 bigger | [1, 2, 3, 0, 0, 6] |
| 2 | i=2 j=1 k=4 | nums1[2]=3 vs nums2[1]=5 → 5 bigger | [1, 2, 3, 0, 5, 6] |
| 3 | i=2 j=0 k=3 | nums1[2]=3 vs nums2[0]=2 → 3 bigger | [1, 2, 3, 3, 5, 6] |
| 4 | i=1 j=0 k=2 | nums1[1]=2 vs nums2[0]=2 → tie, take nums2 | [1, 2, 2, 3, 5, 6] |
| 5 | j = -1 | loop ends, nothing left in nums2 | [1, 2, 2, 3, 5, 6] |
Now let us walk through each turn slowly, following where every pointer moves.
Look at step five closely. Once j drops below zero, nums2 is empty. The values 1 and 2 at the front of nums1 never moved, because they were already in their correct spots. So we stop early, and the array is done as [1, 2, 2, 3, 5, 6].

The sketch above shows the same trace. The two arrays sit on top, and each step shows the bigger value dropping into the back slot. Many people find this picture easier than the table, so use whichever clicks for you.
All three approaches give the correct answer. They just pay different prices. Here is the plain comparison.
| Approach | Time | Space | Note |
|---|---|---|---|
| Dump and sort | O((m+n) log(m+n)) | O(1) | Simple, but ignores that inputs are sorted |
| Merge into copy | O(m + n) | O(m) | Fast, but needs an extra copy array |
| Three pointer | O(m + n) | O(1) | Fastest and in place, fills from the back |
In an interview, mention dump and sort first to show you understand the problem. Then bring up the copy merge to show you can use the sorted order. Finally reach for the three-pointer trick to show the best answer with no extra space. That path from blunt to elegant is exactly what interviewers want to see.
| 💡 Interview Insight If the interviewer says memory is tight, the three-pointer trick is your answer. It uses no extra array and still runs in linear time. Saying that trade-off out loud signals real judgment, not just memorized Big-O. |
A few small traps catch beginners on this problem. Keep these in mind.
The Merge Sorted Array problem DSA looks like a warm-up, and it is. But the fill-from-the-back idea inside it is a real skill. You will reuse that same trick in merge sort, in linked list merges, and in many two-pointer problems.
So take the lesson, not just the answer. Try dump and sort first to be safe. Reach for the copy merge when you want linear time and clarity. Then use the three-pointer trick when you want the fastest in-place answer. Once that pattern feels natural, a whole family of merge problems opens up for you.
You now have Merge Sorted Array in your toolkit, so let us keep the momentum going. Here is where you came from and where you head next in this series.
javahandson.com | DSA Series | Arrays & Strings