Merge Sort
What is Merge Sort?
Merge sort is a classic Divide and Conquer sorting algorithm. It recursively splits the array into halves until each half has one element (trivially sorted), then merges the sorted halves back together. It guarantees O(n log n) in all cases.
| Case | Time | Space | Stable? |
|---|---|---|---|
| Best / Average / Worst | O(n log n) | O(n) | Yes |
How it Works
For array [38, 27, 43, 3, 9, 82, 10]:
- Divide: [38,27,43,3] | [9,82,10] → [38,27] | [43,3] | [9,82] | [10] → [38] | [27] | [43] | [3] | [9] | [82] | [10]
- Merge: [27,38] | [3,43] | [9,82] | [10] → [3,27,38,43] | [9,10,82] → [3,9,10,27,38,43,82] ✓
import java.util.Arrays;
public class MergeSort {
// Main sort function — DIVIDE step
static void mergeSort(int[] arr, int left, int right) {
if (left >= right) return; // base case: 0 or 1 element
int mid = left + (right - left) / 2; // avoid overflow
mergeSort(arr, left, mid); // sort left half
mergeSort(arr, mid + 1, right); // sort right half
merge(arr, left, mid, right); // COMBINE step
}
// Merge two sorted subarrays arr[left..mid] and arr[mid+1..right]
static void merge(int[] arr, int left, int mid, int right) {
int n1 = mid - left + 1;
int n2 = right - mid;
// Create temp arrays
int[] L = new int[n1];
int[] R = new int[n2];
for (int i = 0; i < n1; i++) L[i] = arr[left + i];
for (int j = 0; j < n2; j++) R[j] = arr[mid + 1 + j];
// Merge back into arr
int i = 0, j = 0, k = left;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) arr[k++] = L[i++];
else arr[k++] = R[j++];
}
while (i < n1) arr[k++] = L[i++];
while (j < n2) arr[k++] = R[j++];
}
// Count inversions using merge sort (bonus)
static long countInversions(int[] arr, int left, int right) {
long count = 0;
if (left < right) {
int mid = (left + right) / 2;
count += countInversions(arr, left, mid);
count += countInversions(arr, mid + 1, right);
count += mergeCount(arr, left, mid, right);
}
return count;
}
static long mergeCount(int[] arr, int left, int mid, int right) {
int[] L = Arrays.copyOfRange(arr, left, mid + 1);
int[] R = Arrays.copyOfRange(arr, mid + 1, right + 1);
int i = 0, j = 0, k = left;
long count = 0;
while (i < L.length && j < R.length) {
if (L[i] <= R[j]) arr[k++] = L[i++];
else { arr[k++] = R[j++]; count += L.length - i; }
}
while (i < L.length) arr[k++] = L[i++];
while (j < R.length) arr[k++] = R[j++];
return count;
}
public static void main(String[] args) {
int[] arr = {38, 27, 43, 3, 9, 82, 10};
System.out.println("Before: " + Arrays.toString(arr));
mergeSort(arr, 0, arr.length - 1);
System.out.println("After: " + Arrays.toString(arr));
// After: [3, 9, 10, 27, 38, 43, 82]
int[] inv = {2, 4, 1, 3, 5};
System.out.println("Inversions: " + countInversions(inv, 0, inv.length - 1)); // 3
}
}
/*
* Recurrence: T(n) = 2T(n/2) + O(n)
* Master Theorem Case 2: T(n) = O(n log n)
*
* Advantages:
* - Guaranteed O(n log n) — no worst case like Quick Sort
* - Stable sort — preserves relative order of equal elements
* - Good for linked lists (no random access needed)
*
* Disadvantage:
* - O(n) extra space for temporary arrays
*/
Merge Sort Advantages and Disadvantages
| Advantages | Disadvantages |
|---|---|
| Guaranteed O(n log n) — no worst case | O(n) extra space for temporary arrays |
| Stable sort — preserves relative order of equal elements | Slower than Quick Sort in practice (cache misses) |
| Excellent for linked lists (no random access needed) | Not in-place |
| Parallelizable — subproblems are independent | Overkill for small arrays |
| Used in external sorting (data too large for RAM) | — |