Merge sort
In computer science, merge sort is an efficient and general purpose comparison-based sorting algorithm. Most implementations of merge sort are stable, which means that the relative order of equal elements is the same between the input and output. Merge sort is a divide-and-conquer algorithm that was invented by John von Neumann in 1945. A detailed description and analysis of bottom-up merge sort appeared in a report by Goldstine and von Neumann as early as 1948.
Algorithm
Conceptually, a merge sort works as follows:- Divide the unsorted list into n sub-lists, each containing one element.
- Repeatedly merge sublists to produce new sorted sublists until there is only one sublist remaining. This will be the sorted list.
Top-down implementation
As a simple example, consider an array with two elements. The elements are copied to
B, then merged back to A. If there are four elements, when the bottom of the recursion level is reached, single element runs from A are merged to B, and then at the next higher level of recursion, those two-element runs are merged to A. This pattern continues with each level of recursion.// Array A has the items to sort; array B is a work array.
void TopDownMergeSort
// Split A into 2 runs, sort both runs into B, merge both runs from B to A
// iBegin is inclusive; iEnd is exclusive.
void TopDownSplitMerge
// Left source half is A.
// Right source half is A.
// Result is B.
void TopDownMerge
void CopyArray
Sorting the entire array is accomplished by.
Bottom-up implementation
Example C-like code using indices for bottom-up merge sort algorithm which treats the list as an array of n sublists of size 1, and iteratively merges sub-lists back and forth between two buffers:// array A has the items to sort; array B is a work array
void BottomUpMergeSort
// Left run is A.
// Right run is A.
void BottomUpMerge
void CopyArray
Top-down implementation using lists
for top-down merge sort algorithm which recursively divides the input list into smaller sublists until the sublists are trivially sorted, and then merges the sublists while returning up the call chain.function merge_sort is
// Base case. A list of zero or one elements is sorted, by definition.
if length of m ≤ 1 then
return m
// Recursive case. First, divide the list into equal-sized sublists
// consisting of the first half and second half of the list.
// This assumes lists start at index 0.
var left := empty list
var right := empty list
for each x with index i in m do
if i < /2 then
add x to left
else
add x to right
// Recursively sort both sublists.
left := merge_sort
right := merge_sort
// Then merge the now-sorted sublists.
return merge
In this example, the function merges the left and right sublists.
function merge is
var result := empty list
while left is not empty and right is not empty do
if first ≤ first then
append first to result
left := rest
else
append first to result
right := rest
// Either left or right may have elements left; consume them.
// '
while left is not empty do
append first to result
left := rest
while right is not empty do
append first to result
right := rest
return''' result
Bottom-up implementation using lists
for bottom-up merge sort algorithm which uses a small fixed size array of references to nodes, wherearray is either a reference to a list of size 2i or nil. node is a reference or pointer to a node. The merge function would be similar to the one shown in the top-down merge lists example, it merges two already sorted lists, and handles empty lists. In this case, merge would use node for its input parameters and return value.function merge_sort is
// return if empty list
if head = nil then
return nil
var node array; initially all nil
var node result
var node next
var int i
result := head
// merge nodes into array
while result ≠ nil do
next := result.next;
result.next := nil
for && do
result := merge
array := nil
// do not go past end of array
if i = 32 then
i -= 1
array := result
result := next
// merge array into single list
result := nil
for 'do
result := merge
return' result
Top-down implementation in a declarative style
-like pseudocode, showing how merge sort can be implemented in such a language using constructs and ideas from functional programming.mergeSort :: Ord a => ->
mergeSort =
mergeSort =
mergeSort xs = merge
where = splitAt xs
merge :: Ord a => ->
merge = xs
merge = xs
merge | x <= y = x : merge
| otherwise = y : merge
Analysis
In sorting n objects, merge sort has an average and worst-case performance of O comparisons. If the running time of merge sort for a list of length n is T, then the recurrence relation T = 2T + n follows from the definition of the algorithm. The closed form follows from the master theorem for divide-and-conquer recurrences.The number of comparisons made by merge sort in the worst case is given by the sorting numbers. These numbers are equal to or slightly smaller than, which is between and. Merge sort's best case takes about half as many iterations as its worst case.
For large n and a randomly ordered input list, merge sort's expected number of comparisons approaches α·''n fewer than the worst case, where
In the worst case, merge sort uses approximately 39% fewer comparisons than quicksort does in its average'' case, and in terms of moves, merge sort's worst case complexity is O - the same complexity as quicksort's best case.
Merge sort is more efficient than quicksort for some types of lists if the data to be sorted can only be efficiently accessed sequentially, and is thus popular in languages such as Lisp, where sequentially accessed data structures are very common. Unlike some implementations of quicksort, merge sort is a stable sort.
Merge sort's most common implementation does not sort in place; therefore, the memory size of the input must be allocated for the sorted output to be stored in.
Natural merge sort
A natural merge sort is similar to a bottom-up merge sort except that any naturally occurring runs in the input are exploited. Both monotonic and bitonic runs may be exploited, with lists being convenient data structures. In the bottom-up merge sort, the starting point assumes each run is one item long. In practice, random input data will have many short runs that just happen to be sorted. In the typical case, the natural merge sort may not need as many passes because there are fewer runs to merge. In the best case, the input is already sorted, so the natural merge sort need only make one pass through the data. In many practical cases, long natural runs are present, and for that reason natural merge sort is exploited as the key component of Timsort. Example:Start : 3 4 2 1 7 5 8 9 0 6
Select runs :
Merge :
Merge :
Merge :
Formally, the natural merge sort is said to be Runs-optimal, where is the number of runs in, minus one.
Tournament replacement selection sorts are used to gather the initial runs for external sorting algorithms.
Ping-pong merge sort
Instead of merging two blocks at a time, a ping-pong merge merges four blocks at a time. The four sorted blocks are merged simultaneously to auxiliary space into two sorted blocks, then the two sorted blocks are merged back to main memory. Doing so omits the copy operation and reduces the total number of moves by half. An early public domain implementation of a four-at-once merge was by WikiSort in 2014, the method was later that year described as an optimization for patience sorting and named a ping-pong merge. Quadsort implemented the method in 2020 and named it a quad merge.In-place merge sort
One drawback of merge sort, when implemented on arrays, is its working memory requirement. Several methods to reduce memory or make merge sort fully in-place have been suggested:- suggested an alternative version of merge sort that uses constant additional space.
- Katajainen et al. present an algorithm that requires a constant amount of working memory: enough storage space to hold one element of the input array, and additional space to hold pointers into the input array. They achieve an time bound with small constants, but their algorithm is not stable.
- Several attempts have been made at producing an in-place merge algorithm that can be combined with a standard merge sort to produce an in-place merge sort. In this case, the notion of "in-place" can be relaxed to mean "taking logarithmic stack space", because standard merge sort requires that amount of space for its own stack usage. It was shown by Geffert et al. that in-place, stable merging is possible in time using a constant amount of scratch space, but their algorithm is complicated and has high constant factors: merging arrays of length and can take moves. This high constant factor and complicated in-place algorithm was made simpler and easier to understand. Bing-Chao Huang and Michael A. Langston presented a straightforward linear time algorithm practical in-place merge to merge a sorted list using fixed amount of additional space. They both have used the work of Kronrod and others. It merges in linear time and constant extra space. The algorithm takes little more average time than standard merge sort algorithms, free to exploit temporary extra memory cells, by less than a factor of two. Though the algorithm is much faster in a practical way, it is unstable for some lists. But using similar concepts, they have been able to solve this problem. Other in-place algorithms include SymMerge, which takes time in total and is stable. Plugging such an algorithm into merge sort increases its complexity to the non-linearithmic, but still quasilinear,.
- Many applications of external sorting use a form of merge sorting where the input gets split up to a higher number of sublists, ideally to a number for which merging them still makes the currently processed set of pages fit into main memory.
- A modern stable, linear, and in-place merge variant is block merge sort, which creates a section of unique values to use as swap space.
- The space overhead can be reduced to by using binary searches and rotations. This method is employed by the C++ STL library and quadsort.
- An alternative to reduce the copying into multiple lists is to associate a new field of information with each key. This field will be used to link the keys and any associated information together in a sorted list. Then the merging of the sorted lists proceeds by changing the link values; no records need to be moved at all. A field which contains only a link will generally be smaller than an entire record so less space will also be used. This is a standard sorting technique, not restricted to merge sort.
- A simple way to reduce the space overhead to n/2 is to maintain left and right as a combined structure, copy only the left part of m into temporary space, and to direct the merge routine to place the merged output into m. With this version it is better to allocate the temporary space outside the merge routine, so that only one allocation is needed. The excessive copying mentioned previously is also mitigated, since the last pair of lines before the return result statement become superfluous.