Solution: Merge Sorted Array
Let's solve the Merge Sorted Array problem using the K-way Merge pattern.
Statement
Given two sorted integer arrays, and , and the number of data elements in each array, and , implement a function that merges the second array into the first one. You have to modify in place.
Note: Assume that has a size equal to , meaning it has enough space to hold additional elements from .
Constraints:
nums1.length
nums2.length
-
nums1[i]
,nums2[j]
Solution
So far, you’ve probably brainstormed some approaches and have an idea of how to solve this problem. Let’s explore some of these approaches and figure out which one to follow based on considerations such as time complexity and any implementation constraints.
Naive approach
The naive approach here is to append the second list to the first—at a cost of —and then sort it using quick sort—at a cost of —for an overall time complexity of .
Optimized approach using k-way merge
This approach uses backward traversal to merge two sorted arrays into the first array in place. Initially, two pointers are set to point at the last elements of both arrays. A third pointer is also positioned at the end of the first array, pointing to the empty location. It compares the elements at the first and second pointer and checks the larger among them. The larger one is placed at the location pointed by the third pointer. Then pointers are adjusted, decrementing the third one and moving the first or second pointer one step back depending on the larger element. This ensures that the smallest element among the compared elements can be further considered in the merging process. This backward merging continues until all elements of the second array have been placed into the first array, resulting in a fully merged, sorted array.
With the k-way merge approach, we iterate over our given arrays using two pointers and merge them in place. The time complexity for this is , which is more efficient than the complexity of the naive approach.
The slides below illustrate how we would like the algorithm to run:
Note: In the following section, we will gradually build the solution. Alternatively, you can skip straight to just the code.
Step-by-step solution construction
First, we initialize two pointers, p1
and p2
, that point to the last element of each array. p1
is initialized to m - 1
, the index of the last data element in nums1
, and p2
is initialized to n - 1
, the index of the last data element in nums2
.
class MergeSorted {public static String printArraywithMarkers(int[] arr, int pValue) {String out = "[";for (int i = 0; i < arr.length - 1; i++) {if (i == pValue) {out += "«";out += String.valueOf(arr[i]) + "»" + ", ";}else {out += String.valueOf(arr[i]) + ", ";}}if (pValue == arr.length - 1) {out += "«" + arr[arr.length - 1];out += "»]";}else {out += arr[arr.length - 1];out += "]";}return out;}public static void mergeSorted(int[] nums1, int m, int[] nums2, int n) {int p1 = m - 1; // set p1 to the last initialised element of nums1int p2 = n - 1; // set p2 to the last element of nums2System.out.println("\n\t\tInitialising p1 to the last data element of nums1:");System.out.println("\t\t\t" + printArraywithMarkers(nums1, p1));System.out.println("\t\t\tp1: " + p1 + ", value: " + nums1[p1]);System.out.println("\n\t\tInitialising p2 to the last data element of nums2:");System.out.println("\t\t\t" + printArraywithMarkers(nums2, p2));System.out.println("\t\t\tp1: " + p2 + ", value: " + nums2[p2]);}public static void main(String args[]) {int[] m = {9, 2, 3, 1, 8};int[] n = {6, 1, 4, 2, 1};int[][] nums1 = {{23, 33, 35, 41, 44, 47, 56, 91, 105, 0, 0, 0, 0, 0, 0},{1, 2, 0},{1, 1, 1, 0, 0, 0, 0},{6, 0, 0},{12, 34, 45, 56, 67, 78, 89, 99, 0}};int[][] nums2 = {{32, 49, 50, 51, 61, 99},{7},{1, 2, 3, 4},{- 45, -99},{100}};int k = 1;for (int i = 0; i < m.length; i++) {System.out.print(k + ".\tnums1: [");for (int j = 0; j < nums1[i].length - 1; j++) {System.out.print(nums1[i][j] + ", ");}System.out.println(nums1[i][nums1[i].length - 1] + "], m: " + m[i]);System.out.print("\tnums2: [");for (int j = 0; j < nums2[i].length - 1; j++) {System.out.print(nums2[i][j] + ", ");}System.out.println(nums2[i][nums2[i].length - 1] + "], n: " + n[i]);mergeSorted(nums1[i], m[i], nums2[i], n[i]);System.out.println("\tMerged list: None");System.out.println(PrintHyphens.repeat("-", 100));k += 1;}}}
Next, we initialize another pointer called p
, which points to the m + n - 1
index of nums1
. We use the pointer p
to traverse the nums1
array from the end. If the value at p1
is greater than the value at p2
, the result at p
is equal to the value at p1
. We then decrement p1
to move it one element back.
class MergeSorted {public static String printArraywithMarkers(int[] arr, int pValue) {String out = "[";for (int i = 0; i < arr.length - 1; i++) {if (i == pValue) {out += "«";out += String.valueOf(arr[i]) + "»" + ", ";}else {out += String.valueOf(arr[i]) + ", ";}}if (pValue == arr.length - 1) {out += "«" + arr[arr.length - 1];out += "»]";}else {out += arr[arr.length - 1];out += "]";}return out;}public static String printArraywithMarkers(int[] arr, int iValue, int jValue) {String out = "[";for (int i = 0; i < arr.length - 1; i++) {if (i == iValue || i == jValue) {out += "«";out += String.valueOf(arr[i]) + "»" + ", ";}else {out += String.valueOf(arr[i]) + ", ";}}if (iValue == arr.length - 1 || jValue == arr.length - 1) {out += "«" + arr[arr.length - 1];out += "»]";}else {out += arr[arr.length - 1];out += "]";}return out;}public static int[] mergeSorted(int[] nums1, int m, int[] nums2, int n) {int p1 = m - 1; // set p1 to the last initialised element of nums1int p2 = n - 1; // set p2 to the last element of nums2System.out.println("\n\t\tInitialising p1 to the last data element of nums1:");System.out.println("\t\t\t" + printArraywithMarkers(nums1, p1));System.out.println("\t\t\tp1: " + p1 + ", value: " + nums1[p1]);System.out.println("\n\t\t Initialising p2 to the last data element of nums2:");System.out.println("\t\t\t" + printArraywithMarkers(nums2, p2));System.out.println("\t\t\tp1: " + p2 + ", value: " + nums2[p2]);// traverse backwards over the nums1 arraySystem.out.println("\n\tTraversing the nums1 array using pointer p:");int x = 0;for (int p = m + n - 1; p >= 0; p--) {System.out.println("\t\tLoop iteration: " + x);x += 1;System.out.println("\t\t\t" + printArraywithMarkers(nums1, p1));System.out.print("\t\t\tp1: " + p1 + ", value: ");if (p1 < 0) System.out.println(nums1[p1 + 1]);else System.out.println(nums1[p1]);System.out.println("\t\t\t" + printArraywithMarkers(nums2, p2));System.out.print("\t\t\tp2: " + p2 + ", value: ");if (p2 < 0) System.out.println(nums2[p2 + 1]);else System.out.println(nums2[p2]);// if p1 is not on the first element and the nums1 element is greater than nums2 elementif (p1 >= 0 && nums1[p1] > nums2[p2]) {System.out.println("\t\t\tThe value at p1(" + nums1[p1] + ") > the value at p2(" + nums2[p2] + ")");// set the element at index p = the element on index p1 in nums1System.out.println("\t\t\t\t" + printArraywithMarkers(nums1, p1, p));System.out.println("\t\t\t\tp1: " + p1 + ", value: " + nums1[p1]);System.out.println("\t\t\t\tp: " + p + ", value: " + nums1[p]);nums1[p] = nums1[p1];System.out.println("\t\t\t\tChanging the value at p to the value at p1:");System.out.println("\t\t\t\t\t" + printArraywithMarkers(nums1, p));// move the p1 pointer one element backSystem.out.println("\t\t\t\tDecrementing p1: " + p1 + " ⟶ " + (p1 - 1) + "\n");p1 -= 1;}}return nums1;}public static void main(String args[]) {int[] m = {9, 2, 3, 1, 8};int[] n = {6, 1, 4, 2, 1};int[][] nums1 = {{23, 33, 35, 41, 44, 47, 56, 91, 105, 0, 0, 0, 0, 0, 0},{1, 2, 0},{1, 1, 1, 0, 0, 0, 0},{6, 0, 0},{12, 34, 45, 56, 67, 78, 89, 99, 0}};int[][] nums2 = {{32, 49, 50, 51, 61, 99},{7},{1, 2, 3, 4},{- 45, -99},{100}};int k = 1;for (int i = 0; i < m.length; i++) {System.out.print(k + ".\tnums1: [");for (int j = 0; j < nums1[i].length - 1; j++) {System.out.print(nums1[i][j] + ", ");}System.out.println(nums1[i][nums1[i].length - 1] + "], m: " + m[i]);System.out.print("\tnums2: [");for (int j = 0; j < nums2[i].length - 1; j++) {System.out.print(nums2[i][j] + ", ");}System.out.println(nums2[i][nums2[i].length - 1] + "], n: " + n[i]);mergeSorted(nums1[i], m[i], nums2[i], n[i]);System.out.println("\tMerged list: None");System.out.println(PrintHyphens.repeat("-", 100));k += 1;}}}
If the value at p1
is less than the value at p2
, the result at p
is set equal to the value at p2
. We decrement the pointer for the list that the entry was copied from.
The traversal continues until nums2
is merged with nums1
.
class MergeSorted {public static String printArraywithMarkers(int[] arr, int pValue) {String out = "[";for (int i = 0; i < arr.length - 1; i++) {if (i == pValue) {out += "«";out += String.valueOf(arr[i]) + "»" + ", ";}else {out += String.valueOf(arr[i]) + ", ";}}if (pValue == arr.length - 1) {out += "«" + arr[arr.length - 1];out += "»]";}else {out += arr[arr.length - 1];out += "]";}return out;}public static String printArraywithMarkers(int[] arr, int iValue, int jValue) {String out = "[";for (int i = 0; i < arr.length - 1; i++) {if (i == iValue || i == jValue) {out += "«";out += String.valueOf(arr[i]) + "»" + ", ";}else {out += String.valueOf(arr[i]) + ", ";}}if (iValue == arr.length - 1 || jValue == arr.length - 1) {out += "«" + arr[arr.length - 1];out += "»]";}else {out += arr[arr.length - 1];out += "]";}return out;}public static int[] mergeSorted(int[] nums1, int m, int[] nums2, int n) {int p1 = m - 1; // set p1 to the last initialised element of nums1int p2 = n - 1; // set p2 to the last element of nums2System.out.println("\n\t\tInitialising p1 to the last data element of nums1:");System.out.println("\t\t\t" + printArraywithMarkers(nums1, p1));System.out.println("\t\t\tp1: " + p1 + ", value: " + nums1[p1]);System.out.println("\n\t\t Initialising p2 to the last data element of nums2:");System.out.println("\t\t\t" + printArraywithMarkers(nums2, p2));System.out.println("\t\t\tp1: " + p2 + ", value: " + nums2[p2]);// traverse backwards over the nums1 arraySystem.out.println("\n\tTraversing the nums1 array using pointer p:");int x = 0;for (int p = m + n - 1; p >= 0; p--) {System.out.println("\t\tLoop iteration: " + x);if (p2 < 0) {System.out.println("\t\t\tBreaking the loop since p2(" + p2 + ") < 0");break;}else if (p1 < 0) {System.out.println("\t\t\tp1: " + p1 + " as we've traversed the entire nums1 array");System.out.println("\t\t\t" + printArraywithMarkers(nums2, p2));System.out.println("\t\t\tp2: " + p2 + ", value: " + nums2[p2]);}else {System.out.println("\t\t\t" + printArraywithMarkers(nums1, p1));System.out.println("\t\t\tp1: " + p1 + ", value: " + nums1[p1]);System.out.println("\t\t\t" + printArraywithMarkers(nums2, p2));System.out.println("\t\t\tp2: " + p2 + ", value: " + nums2[p2]);}x += 1;// if p1 is not on the first element and the nums1 element is greater than nums2 elementif (p1 >= 0 && nums1[p1] > nums2[p2]) {System.out.println("\t\t\tThe value at p1(" + nums1[p1] + ") > the value at p2(" + nums2[p2] + ")");// set the element at index p = the element on index p1 in nums1System.out.println("\t\t\t\t" + printArraywithMarkers(nums1, p1, p));System.out.println("\t\t\t\tp1: " + p1 + ", value: " + nums1[p1]);System.out.println("\t\t\t\tp: " + p + ", value: " + nums1[p]);nums1[p] = nums1[p1];System.out.println("\t\t\t\tChanging the value at p to the value at p1:");System.out.println("\t\t\t\t\t" + printArraywithMarkers(nums1, p));// move the p1 pointer one element backSystem.out.println("\t\t\t\tDecrementing p1: " + p1 + " ⟶ " + (p1 - 1) + "\n");p1 -= 1;}else {if (p1 > 0) System.out.println("\t\t\tThe value at p1(" + nums1[p1] + ") <= the value at p2(" + nums2[p2] + ")");else System.out.println("\t\t\tMerging nums2 with nums1");System.out.println("\t\t\t\t" + printArraywithMarkers(nums2, p2));System.out.println("\t\t\t\tp2: " + p2 + ", value: " + nums2[p2]);System.out.println("\t\t\t\t" + printArraywithMarkers(nums1, p));System.out.println("\t\t\t\tp: " + p + ", value: " + nums1[p]);// set the nums1 element at index p = nums2 element at index p2nums1[p] = nums2[p2];System.out.println("\t\t\t\tChanging the value at p to the value at p2:");System.out.println("\t\t\t\t\t" + printArraywithMarkers(nums1, p));// move the p2 pointer one element backSystem.out.println("\t\t\t\tDecrementing p2: " + p2 + " ⟶ " + (p2 - 1) + "\n");p2 -= 1;}}return nums1;}public static void main(String args[]) {int[] m = {9, 2, 3, 1, 8};int[] n = {6, 1, 4, 2, 1};int[][] nums1 = {{23, 33, 35, 41, 44, 47, 56, 91, 105, 0, 0, 0, 0, 0, 0},{1, 2, 0},{1, 1, 1, 0, 0, 0, 0},{6, 0, 0},{12, 34, 45, 56, 67, 78, 89, 99, 0}};int[][] nums2 = {{32, 49, 50, 51, 61, 99},{7},{1, 2, 3, 4},{- 45, -99},{100}};int k = 1;for (int i = 0; i < m.length; i++) {System.out.print(k + ".\tnums1: [");for (int j = 0; j < nums1[i].length - 1; j++) {System.out.print(nums1[i][j] + ", ");}System.out.println(nums1[i][nums1[i].length - 1] + "], m: " + m[i]);System.out.print("\tnums2: [");for (int j = 0; j < nums2[i].length - 1; j++) {System.out.print(nums2[i][j] + ", ");}System.out.println(nums2[i][nums2[i].length - 1] + "], n: " + n[i]);System.out.println("\tMerged list:" + Arrays.toString(mergeSorted(nums1[i], m[i], nums2[i], n[i])));System.out.println(PrintHyphens.repeat("-", 100));k += 1;}}}
Just the code
Here’s the complete solution to this problem:
class MergeSorted {public static int[] mergeSorted(int[] nums1, int m, int[] nums2, int n) {int p1 = m - 1;int p2 = n - 1;for (int p = m + n - 1; p >= 0; p--) {if (p2 < 0) {break;}if (p1 >= 0 && nums1[p1] > nums2[p2]) {nums1[p] = nums1[p1];p1 -= 1;}else {nums1[p] = nums2[p2];p2 -= 1;}}return nums1;}// Driver codepublic static void main(String args[]) {int[] m = {9, 2, 3, 1, 8};int[] n = {6, 1, 4, 2, 1};int[][] nums1 = {{23, 33, 35, 41, 44, 47, 56, 91, 105, 0, 0, 0, 0, 0, 0},{1, 2, 0},{1, 1, 1, 0, 0, 0, 0},{6, 0, 0},{12, 34, 45, 56, 67, 78, 89, 99, 0}};int[][] nums2 = {{32, 49, 50, 51, 61, 99},{7},{1, 2, 3, 4},{- 45, -99},{100}};int k = 1;for (int i = 0; i < m.length; i++) {System.out.print(k + ".\tnums1: [");for (int j = 0; j < nums1[i].length - 1; j++) {System.out.print(nums1[i][j] + ", ");}System.out.println(nums1[i][nums1[i].length - 1] + "], m: " + m[i]);System.out.print("\tnums2: [");for (int j = 0; j < nums2[i].length - 1; j++) {System.out.print(nums2[i][j] + ", ");}System.out.println(nums2[i][nums2[i].length - 1] + "], n: " + n[i]);System.out.println("\tMerged list:" + Arrays.toString(mergeSorted(nums1[i], m[i], nums2[i], n[i])));System.out.println(PrintHyphens.repeat("-", 100));k += 1;}}}
Solution summary
To recap, the solution to this problem can be divided into the following parts:
- Initialize two pointers that point to the last data elements in both arrays.
- Initialize a third pointer that points to the last index of .
- Traverse from the end using the third pointer and compare the values corresponding to the first two pointers.
- Place the larger of the two values at the third pointer’s index.
- Repeat the process until the two arrays are merged.
Time complexity
The time complexity is , where and are the counts of initialized elements in the two arrays.
Space complexity
The space complexity is because we only use the space required for three indices.
Level up your interview prep. Join Educative to access 70+ hands-on prep courses.