Merge Sort
Overview
Merge Sort is a highly efficient, elegant Divide and Conquer algorithm. It is famously known for guaranteeing O(N log N) time complexity in all scenarios (Best, Worst, and Average).
The algorithm operates in two phases: 1. Divide: Recursively divide the unsorted array in half until you have sub-arrays containing exactly 1 element. (An array of 1 element is inherently sorted). 2. Conquer (Merge): Take two sorted sub-arrays, compare their elements one by one, and merge them into a single, newly sorted array. Repeat this as you travel back up the recursive tree.
Because it guarantees O(N log N) performance, it is significantly safer than Quick Sort for massive, unpredictable datasets. However, this safety comes at a cost: the 'Merge' phase requires creating temporary arrays, resulting in an O(N) Space Complexity.
Syntax
public class MergeSort {
// Main recursive function
public static void sort(int[] arr, int left, int right) {
if (left < right) {
int mid = left + (right - left) / 2; // Find middle safely
// 1. Divide: Recursively sort left and right halves
sort(arr, left, mid);
sort(arr, mid + 1, right);
// 2. Conquer: Merge the sorted halves
merge(arr, left, mid, right);
}
}
// The engine of the algorithm: merging two sorted sub-arrays
private static void merge(int[] arr, int left, int mid, int right) {
// Create temporary arrays
int[] L = new int[mid - left + 1];
int[] R = new int[right - mid];
// Copy data to temp arrays
for (int i = 0; i < L.length; i++) L[i] = arr[left + i];
for (int j = 0; j < R.length; j++) R[j] = arr[mid + 1 + j];
// Merge the temp arrays back into the original arr
int i = 0, j = 0, k = left;
while (i < L.length && j < R.length) {
if (L[i] <= R[j]) {
arr[k++] = L[i++];
} else {
arr[k++] = R[j++];
}
}
// Copy any remaining elements
while (i < L.length) arr[k++] = L[i++];
while (j < R.length) arr[k++] = R[j++];
}
}Common Pitfalls
- The O(N) memory allocation inside the recursive loop. Creating
new int[]inside themergefunction thousands of times generates massive Garbage Collection overhead. In professional implementations, a single temporary array of size N is created once and passed down through the recursive calls. - Off-by-one errors in index math. Managing
mid,mid + 1,L.length, andleft + irequires intense focus. A single+ 1in the wrong place will result in ArrayOutOfBounds crashes or corrupted data. - Using it for in-memory embedded systems. Because Merge Sort requires an extra O(N) chunk of memory, running it on a tiny microcontroller to sort a large array will crash the system out of memory. Use In-Place algorithms like Quick Sort or Heap Sort there.
Interview Questions
Linked Lists do not support random access (arr[i]), making Quick Sort's partitioning very slow. However, Merge Sort doesn't need random access! It just sequentially traverses the halves and merges them by re-wiring pointers. Furthermore, when applied to a Linked List, Merge Sort requires NO extra O(N) space, making it O(N log N) time and O(1) space.
Yes. When merging the two halves, if we have duplicate elements, the logic if (L[i] <= R[j]) ensures that the element from the left half (which originally came first) is placed in the merged array first. This preserves the original relative order.
Real-World Example
External Sorting (Big Data). Imagine you have a 100GB database file that needs sorting, but you only have 4GB of RAM. You cannot load it all into memory. You read 4GB chunks, sort them in RAM, and write them to 25 temporary files. Then, you use the 'Merge' phase of Merge Sort to stream the top lines of those 25 files, combine them in order, and write directly to the final disk file.
// Concept of K-Way Merge for Big Data
public void externalSort() {
List<File> sortedChunks = createSortedChunks();
PriorityQueue<ChunkReader> pq = new PriorityQueue<>(); // Min-Heap
// Stream just the first element of each chunk into memory
for (File file : sortedChunks) {
pq.offer(new ChunkReader(file));
}
while(!pq.isEmpty()) {
// Extract absolute smallest item, write to disk
ChunkReader smallest = pq.poll();
outputWriter.write(smallest.currentElement());
// Read next item from that specific chunk and push to PQ
if (smallest.hasNext()) pq.offer(smallest.readNext());
}
}Check Your Knowledge
Test your understanding of Merge Sort with these quick questions.