Merge Sort is a classic divide and conquer sorting algorithm. Given an array of integers nums, sort the array in ascending order in O(n log n) time.
Example 1:
Input: nums = [5,2,3,1] Output: [1,2,3,5]
Example 2:
Input: nums = [5,1,1,2,0,0] Output: [0,0,1,1,2,5]
Approach: Merge Sort (Divide and Conquer)
Algorithm
- Divide: Recursively split the array into two halves until each half has a single element
- Conquer: A single-element array is trivially sorted
- Combine: Merge two sorted halves into one sorted array by repeatedly taking the smaller head
- Repeat until the whole array is merged
Time & Space Complexity
- Time Complexity: O(n log n) - each level merges O(n) elements across O(log n) levels
- Space Complexity: O(n) - the temporary array used during merging
Java Implementation
public class MergeSort {
/**
* Sort an array in ascending order using merge sort.
* @param nums Input array
* @return Sorted array
*/
public static int[] sortArray(int[] nums) {
int[] temp = new int[nums.length];
mergeSort(nums, temp, 0, nums.length - 1);
return nums;
}
private static void mergeSort(int[] nums, int[] temp, int lo, int hi) {
if (lo >= hi) {
return;
}
int mid = lo + (hi - lo) / 2;
mergeSort(nums, temp, lo, mid);
mergeSort(nums, temp, mid + 1, hi);
merge(nums, temp, lo, mid, hi);
}
private static void merge(int[] nums, int[] temp, int lo, int mid, int hi) {
for (int i = lo; i <= hi; i++) {
temp[i] = nums[i];
}
int i = lo;
int j = mid + 1;
int k = lo;
while (i <= mid && j <= hi) {
if (temp[i] <= temp[j]) {
nums[k++] = temp[i++];
} else {
nums[k++] = temp[j++];
}
}
while (i <= mid) {
nums[k++] = temp[i++];
}
while (j <= hi) {
nums[k++] = temp[j++];
}
}
// Test method
public static void main(String[] args) {
int[] nums1 = {5, 2, 3, 1};
System.out.println("Input: nums = [5, 2, 3, 1]");
System.out.println("Output: " + java.util.Arrays.toString(sortArray(nums1)));
// Expected: [1, 2, 3, 5]
int[] nums2 = {5, 1, 1, 2, 0, 0};
System.out.println("Input: nums = [5, 1, 1, 2, 0, 0]");
System.out.println("Output: " + java.util.Arrays.toString(sortArray(nums2)));
// Expected: [0, 0, 1, 1, 2, 5]
}
}
Example Walkthrough
For nums = [5, 2, 3, 1]:
- Split:
[5, 2]and[3, 1] - Split again:
[5],[2],[3],[1] - Merge
[5]and[2]->[2, 5] - Merge
[3]and[1]->[1, 3] - Merge
[2, 5]and[1, 3]->[1, 2, 3, 5]
Key Points
- Stable: Equal elements keep their relative order
- Guaranteed O(n log n): Performance does not depend on the input distribution
- Not In-Place: Requires O(n) extra space for the merge step
- Predicate: Used in advanced problems like counting inversions and “Count of Smaller Numbers After Self”
- Recursion Base Case: Arrays of size 0 or 1 are already sorted