Merge Sort

Merge Sort is an $$O(n \log n)$$ sort that is based on divide and conquer. It is stable, but not adaptive, and uses $$O(n)$$ memory.

The Merge
A merge is an operation that takes 2 sorted arrays and merges them into 1 sorted array. One implementation works like this:


 * Compare the first elements of both arrays:
 * If the beginning of the first array is smaller or equal to the second array, move it to the output.
 * If not, then move beginning of the second array to the output.
 * If one array is empty, copy the other one to output.

The Sort
Merge sort works like this: if the array length is 1 or less, the sort is done. Otherwise, merge sort the left half (recursively). Merge sort the right half (recursively). Merge the left and right halves.

Example
Take the array: [6 2  3  8  7  5  1  4] Recursively sort by merging starting with single elements: [6] [2] [3] [8] [7] [5] [1] [4] ↓   ↓   |   |   |   |   |   | [2   6]  ↓   ↓   |   |   |   |  |   |  [3   8]  |   |   |   |  ↓   ↓   ↓   ↓   |   |   |   | [2   3   6   8]  ↓   ↓   |   |                 [5   7]  ↓   ↓                  |   |  [1   4]                  ↓   ↓   ↓   ↓                 [1   4   5   7] The final merge step by step: [2  3  6  8] [1  4  5  7] main array               │ aux array ──────────────────────────┼───────────────────────────────── [2 3  6  8] [1  4  5  7] │ 1 < 2 → [1  0  0  0  0  0  0  0]  ↑            ↑           │          ↑ [2  3  6  8] [1  4  5  7] │ 2 < 4 → [1  2  0  0  0  0  0  0]  ↑               ↑        │             ↑ [2  3  6  8] [1  4  5  7] │ 3 < 4 → [1  2  3  0  0  0  0  0]     ↑            ↑        │                ↑ [2  3  6  8] [1  4  5  7] │ 4 < 6 → [1  2  3  4  0  0  0  0]        ↑         ↑        │                   ↑ [2  3  6  8] [1  4  5  7] │ 5 < 6 → [1  2  3  4  5  0  0  0]        ↑            ↑     │                      ↑ [2  3  6  8] [1  4  5  7] │ 6 < 7 → [1  2  3  4  5  6  0  0]        ↑               ↑  │                         ↑ [2  3  6  8] [1  4  5  7] │ 7 < 8 → [1  2  3  4  5  6  7  0]           ↑            ↑  │                            ↑ [2  3  6  8] [1  4  5  7] │ copy  → [1  2  3  4  5  6  7  8] ↑              │                               ↑ Copy sorted result from aux array to main array: aux array: [1  2  3  4  5  6  7  8] ↓ ↓  ↓  ↓  ↓  ↓  ↓  ↓ main array: [1  2  3  4  5  6  7  8]   sorted!

Merging function
procedure merge(array arr, int start, int mid, int end) do int left = start int right = mid int to = 0 array temp = array(length: end - start) while left < mid and right < end do if array[left] <= array[right] then temp[to] = array[left] left += 1 else temp[to] = array[right] right += 1 to += 1 while left < mid do temp[to] = array[left] left += 1 to += 1 while right < end do temp[to] = array[right] right += 1 to += 1 copy(from: temp, to: array, from_offset: 0, to_offset: start, length: end - start)

Merge sort
procedure mergesort(array arr, range merge_range) do if length(merge_range) >= 2 then int mid = merge_range.midpoint merge_sort(arr, range(merge_range.start, mid)) merge_sort(arr, range(mid, merge_range.end)) merge(arr, merge_range.start, mid, merge_range.end) end

Hybrid Merge sort
Although Merge sort's worst case is asymptotically optimal, there are simpler methods for sorting small arrays such as Bubble sort or Insertion sort. These naive sorting algorithms are preferable due to being simple and in-place therefore improving upon locality and memory overhead.

In most practical implementations of Merge sort, Insertion sort variants are commonly used as well as sorting networks.

Bottom-up Merge sort
This is an iterative variation of Merge sort, which merges sorted groups of elements, using powers of two, starting with 1, 2, 4, 8, 16, and so on. Most practical implementations of Merge sort are implemented as a Bottom-up Merge to avoid $$O(\log n)$$ stack usage.

Unlike a top-down recursive Merge sort, Bottom-up Merge is not guaranteed to split every array size evenly. For non-power-of-2 sized arrays, there is a lopsided merge at the end of the array of sizes $$2^{i}$$ and $$\operatorname{mod}\left(n,2^{i}\right)$$. This normally isn't an issue, but, if one wishes the merges to be more even, there are ways to improve this.

Pseudocode
int j = 1 while j < n do # iterative merge sort int i = 0 while i + 2*j < n do merge at indices i, i+j, and i + 2*j i += 2*j end while if i + j < n do merge at indices i, i+j, and n    end if j *= 2 end while

Fixed-point Math Routine
Let $$n$$ be a non-power-of-2 length to be Bottom-up Merge sorted. To guarantee each merge is as even as possible, in other words, the difference between the two lengths in each merge are no greater than 1, one can observe the indices for each merge step are simply a multiple of $$n$$ divided by a power of two rounded to a whole number. ▁▁▁▁▁▂▂▂▂▃▃▃▃▃▅▅▅▅▅▆▆▆▆▇▇▇▇▇▁▁▁▁▁▂▂▂▂▃▃▃▃▃▅▅▅▅▅▆▆▆▆▇▇▇▇▇ ├───────────────────────────────────────────────┼───────────────────────────────────────────────┤ 0                                         │1 * n/2.0f│                                         n                                            └          ┘ ▁▁▂▂▃▃▃▅▅▆▆▇▇▇▁▁▂▂▃▃▃▅▅▆▆▇▇▇▁▁▂▂▃▃▃▅▅▆▆▇▇▇▁▁▂▂▃▃▃▅▅▆▆▇▇▇ ├───────────────────────┼───────────────────────┼───────────────────────┼───────────────────────┤ 0                 │1 * n/4.0f│            │2 * n/4.0f│            │3 * n/4.0f│                 n                    └          ┘            └          ┘            └          ┘ ▁▂▃▃▅▆▇▁▂▃▃▅▆▇▁▂▃▃▅▆▇▁▂▃▃▅▆▇▁▂▃▃▅▆▇▁▂▃▃▅▆▇▁▂▃▃▅▆▇▁▂▃▃▅▆▇ ├───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤ 0     │1 * n/8.0f││2 * n/8.0f││3 * n/8.0f││4 * n/8.0f││5 * n/8.0f││6 * n/8.0f││7 * n/8.0f│     n        └          ┘└          ┘└          ┘└          ┘└          ┘└          ┘└          ┘ . . . The main loop starts merging lengths of sizes $$<2$$ and works its way up until the final merge of lengths $$\lfloor n/2 \rfloor$$ and $$n-\lfloor n/2\rfloor$$.

There may be some accuracy issues using floating-point numbers, so a fixed-point approach is recommended to guarantee perfect accuracy as well as the same results as the example above.

Min-run Optimization
Another way to make merges more even is to choose a small arbitrary length $$m$$ such that $$m\cdot 2^{k} \approx n, k\in\mathbb{N}$$. By keeping $$m$$ small, one can perform a simple sort such as Insertion sort to sort runs of size $$m$$, and then perform a bottom-up merge routine on the runs. Since there are approximately $$2^{k}$$ runs, the resulting merges will be more even in length. This small constant is called the min-run, courtesy of Timsort.

The min-run is calculated by ceiling dividing $$n$$ by two until it reaches below a certain threshold: int minRun = length of array int threshold = 32 while minRun > threshold do minRun = ceiling(minRun / 2) end while

K-Way Merge sort
(A.K.A. Multi-way Merge sort)

Weaved Merge sort
(Not to be confused with Weave Merge sort)