Merge Sort
The more basic bubble, selection, and insertion sorts are great at smaller data sets. However, because they're quadratic O(n^2)
, they fail badly at huge data sets.
By comparison, the more advanced merge, quick, and radix sorts can improve time complexity up to O(n log n)
.
Merge Sort
Merge sort is a combination of 3 actions: splitting up, sorting, and merging.
Merge sort exploits the fact that arrays of 0 or 1 elements are automatically sorted: []
and [1]
. (Insertion sort also exploits this in the beginning.)
Pseudocode
Here's a rough outline of how it works:
Splitting up: In a divide and conquer pattern, an array is continually split up into smaller and smaller subarrays until you get a bunch of empty arrays or arrays with 1 element. By default, these arrays are sorted.
Sort and merging: Sorted subarrays are merged into larger sorted subarrays, and then those sorted subarrays are merged and sorted.
Merging Subarrays
The magic of merge sort is in the merging of sorted arrays.
The pseudocode for merging already sorted arrays goes like this:
Create a new array.
Check the first value of both sorted input arrays. Insert the smaller value into the new array and move onto the next item for that input array.
Continue to do this until both input arrays reach their last item.
If one input array reaches its last item before the other, the remaining input array should just dump all its values to the end of the new array--because we know those values will be larger!
The code implementation goes something like this:
Implementing Merge Sort
The pseudocode to sort subarrays involves divide and conquer via recursion:
Break up the array into 2 halves.
Continue halving the subarrays until all subarrays have 0 or 1 elements.
Using the
merge
helper function, merge the subarrays back together and return the full sorted array.
The code implementation goes something like this:
Big O
Time complexity
The time complexity of merge sort in every case is O(n log n)
. That means that it doesn't complete nearly sorted arrays sooner. It will treat nearly sorted arrays the same as unsorted arrays.
The decomposition of the array is O(log n)
. An 8-element array requires 3 levels of decomposition, a 16-element array requires 4, a 32-element array requires 5, and so on.
Additionally, the merge
function itself has a time complexity of O(n)
. That's because at any given level of decomposition, there is always n
number of elements--regardless of how many subarrays there may be. [1], [2], [3], [4]
is the same as [1, 2], [3, 4]
and [1, 2, 3, 4]
. We also know that each element goes through exactly 1 comparison operation in the merge
function.
Therefore, at every level of decomposition, there are n
comparisons. And since log n
tells us the number of decompositions, n * log n
or n log n
defines the total number of operations.
In other words, merge sort has O(log n)
decompositions and O(n)
comparisons per decomposition.
Note: O(n log n)
is the best we can hope for from sorting algorithms. The only exception is when you take advantage of a weird quirk of the data types involved. For example, radix sort uses a quirk with numbers, so it only works with numbers.
Space complexity
The space complexity of merge sort is O(n)
because every element becomes its own subarray. That means you're storing n
number of arrays in memory due to decomposition.
Last updated