Merge Sort
A stable divide-and-conquer algorithm that divides the array into halves, recursively sorts them, and then merges the sorted halves.
Algorithm Information
Time Complexity
O(n log n)O(n log n)O(n log n)Space Complexity
O(n)Properties
Algorithm Code
1function mergeSort(arr, left = 0, right = arr.length - 1) {2 if (left < right) {3 // Find the middle point4 const mid = Math.floor((left + right) / 2);5 6 // Recursively sort first and second halves7 mergeSort(arr, left, mid);8 mergeSort(arr, mid + 1, right);9 10 // Merge the sorted halves11 merge(arr, left, mid, right);12 }13 return arr;14}15 16function merge(arr, left, mid, right) {17 // Create temp arrays for left and right subarrays18 const leftArr = arr.slice(left, mid + 1);19 const rightArr = arr.slice(mid + 1, right + 1);20 21 let i = 0, j = 0, k = left;22 23 // Merge the temp arrays back into arr[left..right]24 while (i < leftArr.length && j < rightArr.length) {25 if (leftArr[i] <= rightArr[j]) {26 arr[k] = leftArr[i];27 i++;28 } else {29 arr[k] = rightArr[j];30 j++;31 }32 k++;33 }34 35 // Copy remaining elements of leftArr[], if any36 while (i < leftArr.length) {37 arr[k] = leftArr[i];38 i++;39 k++;40 }41 42 // Copy remaining elements of rightArr[], if any43 while (j < rightArr.length) {44 arr[k] = rightArr[j];45 j++;46 k++;47 }48}Variable State
Call Stack
Array Visualization
Watch how Merge Sort divides the array into smaller parts and merges them back in sorted order
Statistics
Legend
Starting Merge Sort algorithm
How Merge Sort Works
Master the divide-and-conquer paradigm through merge sort's elegant recursive approach and guaranteed O(n log n) performance.
Algorithm Overview
Merge Sort is a divide-and-conquer algorithm that works by recursively dividing the array into smaller subarrays until each subarray contains only one element, then merging these subarrays back together in sorted order. This approach guarantees O(n log n) performance in all cases.
🌲 Divide-and-Conquer Strategy
The algorithm follows three key steps: Divide the array into two halves,Conquer by recursively sorting each half, and Combine by merging the sorted halves. This creates a binary tree structure where each level represents a division, and the merging happens as we return up the recursion tree.
Step-by-Step Process
Divide Phase
Recursively split the array into two halves until each subarray contains only one element. Single elements are considered inherently sorted.
const mid = Math.floor((left + right) / 2);
mergeSort(arr, left, mid);
mergeSort(arr, mid + 1, right);
}
Conquer Phase
The base case is reached when subarrays have one element. At this point, we begin the merging process as the recursive calls return.
Merge Phase
Combine two sorted subarrays into a single sorted array by comparing elements from each subarray and selecting the smaller one.
if (arr[i] <= arr[j]) {
temp[k++] = arr[i++];
} else {
temp[k++] = arr[j++];
}
}
Copy Remaining Elements
After one subarray is exhausted, copy any remaining elements from the other subarray, then copy the merged result back to the original array.
Understanding the Recursion Tree
Merge sort creates a binary tree of recursive calls:
🌳 Tree Analysis
The tree has log n levels (height), and each level processes n elementsduring merging. This gives us the O(n log n) time complexity: n elements × log n levels = O(n log n).
Complete Implementation
Here's the complete merge sort implementation with detailed comments:
function mergeSort(arr, left = 0, right = arr.length - 1) {
// Base case: single element or empty array
if (left >= right) return;
// Divide: find the middle point
const mid = Math.floor((left + right) / 2);
// Conquer: recursively sort both halves
mergeSort(arr, left, mid); // Sort left half
mergeSort(arr, mid + 1, right); // Sort right half
// Combine: merge the sorted halves
merge(arr, left, mid, right);
}
function merge(arr, left, mid, right) {
// Create temporary arrays for left and right subarrays
const leftArr = arr.slice(left, mid + 1);
const rightArr = arr.slice(mid + 1, right + 1);
let i = 0, j = 0, k = left;
// Merge the temporary arrays back into arr[left..right]
while (i < leftArr.length && j < rightArr.length) {
if (leftArr[i] <= rightArr[j]) {
arr[k] = leftArr[i];
i++;
} else {
arr[k] = rightArr[j];
j++;
}
k++;
}
// Copy remaining elements of leftArr[], if any
while (i < leftArr.length) {
arr[k] = leftArr[i];
i++;
k++;
}
// Copy remaining elements of rightArr[], if any
while (j < rightArr.length) {
arr[k] = rightArr[j];
j++;
k++;
}
}
// Example usage:
const numbers = [64, 34, 25, 12, 22, 11, 90];
console.log("Original:", numbers);
mergeSort(numbers);
console.log("Sorted:", numbers);Key Concepts & Properties
✅ Advantages
- •Guaranteed O(n log n): Consistent performance for all inputs
- •Stable sorting: Equal elements maintain their relative order
- •Predictable performance: No worst-case degradation
- •Parallelizable: Recursive calls can run in parallel
- •External sorting: Works well with large datasets on disk
❌ Disadvantages
- •Extra memory: Requires O(n) additional space
- •Not in-place: Cannot sort with constant extra space
- •Recursive overhead: Function call stack can be deep
- •Not adaptive: Doesn't benefit from partially sorted data
- •Cache performance: Non-sequential memory access patterns
🎯 Stability Guarantee
Merge sort is stable because during the merge phase, when two elements are equal, we always take from the left subarray first (arr[i] ≤ arr[j]). This ensures that equal elements from the left subarray appear before equal elements from the right subarray, preserving their original relative order.
Complexity Analysis
Best Case
Even with sorted input, the algorithm still divides and merges all subarrays.
Average Case
Consistent performance regardless of input distribution or initial order.
Worst Case
No input can cause merge sort to perform worse than O(n log n).
Mathematical Analysis
When to Use Merge Sort
✅ Perfect for:
- • Stable sorting required: When relative order of equal elements matters
- • Guaranteed performance: When O(n log n) worst-case is critical
- • Large datasets: Consistent performance on big data
- • External sorting: Sorting data that doesn't fit in memory
- • Parallel processing: Recursive structure enables parallelization
- • Linked lists: Natural fit for linked list sorting
❌ Consider alternatives for:
- • Memory-constrained systems: O(n) extra space requirement
- • Small datasets: Insertion sort may be faster
- • Nearly sorted data: Adaptive algorithms perform better
- • In-place requirement: Use heap sort or in-place quick sort
- • Cache-sensitive applications: Quick sort has better locality
🌐 Real-World Applications
Merge sort is used in Java's Arrays.sort() for object arrays,Python's Timsort (hybrid with merge sort), and external sorting algorithmsfor databases. It's the go-to choice when stability and predictable performance are more important than memory usage.
Divide-and-Conquer Paradigm
Merge sort is a perfect example of the divide-and-conquer algorithmic paradigm:
1. Divide
Split the problem into smaller subproblems of the same type
2. Conquer
Solve subproblems recursively until reaching base cases
3. Combine
Merge solutions of subproblems to solve the original problem
🧠 Learning Value
Understanding merge sort teaches fundamental concepts like recursion, divide-and-conquer thinking, and the trade-offs between time and space complexity. It's an excellent introduction to more advanced algorithms and serves as a building block for understanding other divide-and-conquer algorithms.