The merge sort, as the name implies, is achieved by merging two ordered sequences into a large ordered sequence. Merge sort is a typical divide-and-conquer algorithm: first divides the sequence into two parts, then iterates through each part, then merges the results one by one.
The biggest advantage of a merge sort is that it has a time complexity of O (NLGN), which is less than what we had previously chosen to sort and insert. He is also a sort of stability, that is, the relative position of equal elements in the sequence will not change before and after the sort. His only drawback is the need to use an extra n of space to sort.
A principle
The merge sort relies on the merge operation, which merges two sorted sequences into a single sequence, with the following process:
- Apply the space so that it is two of the sum of the sorted sequence, and then copy the array to be sorted into the array.
- Set two pointers where the initial position is the starting position of the two sorted series
- Compares the elements pointed to by two pointers in the copied array, selects the relatively small elements into the original sorted array, and moves the pointer to the next position
- Repeat step 3 until a pointer reaches the end of the sequence
- Copies all remaining elements of another sequence directly to the end of the original array
The process is implemented as follows, and the annotations are clear:
private static void Merge (t[] array, int lo, int mid, int hi) { int i = lo, j = mid + 1; Copies the element to the auxiliary array for (int k = lo; k <= hi; k++) { aux[k] = array[k]; } The data is then copied back from the secondary array to the original array for (int k = lo; k <= hi; k++) { //If the left element is gone, merge the remaining elements of the right to the original array directly into the if (i > Mid) { array[k] = aux[j++]; } If the right element is not there, merge all left remaining elements directly into the original array else if (J > Hi) { array[k] = aux[i++]; } If the left-hand side is small, copy the element on the left to the else if (Aux[i] in the original array. CompareTo (Aux[j]) < 0) { array[k] = aux[i++]; } else { Array[k] = aux[j++];}} }
is to use the above method to combine the two ordered sequences of EEGMR and acert into a large sequence of the process demonstrations:
Two implementations
There are two implementations of merge sort, one is the first and the next (top-down) merge, the other is to the bottom (bottom-up) merge, the two algorithms are similar, here only the first and next merge sort.
The top-down merger is a typical divide-and-conquer algorithm (Divide-and-conquer), and if two sequences are already sequenced, merging the two sequences into a large sequence is the sort of large sequence.
First we divide the elements to be sorted into the left and right two sequences, then sort them into each other, and then merge the ordered sequence with the following code:
public class mergesort<t> where t:icomparable<t>{ private static t[] aux;//auxiliary array for sorting public static void Sort (t[] array) { aux = new T[array. Length]; The Sort is assigned only once (array, 0, array. Length-1); } private static void Sort (t[] array, int lo, int hi) { if (lo >= hi) return,//if subscript is greater than superscript, returns int mid = lo + ( Hi-lo)/2;//array sort (Array, lo, mid),//loop to the left element sort (Array, mid + 1, HI);//loop to the right element sort Merge (array, lo, Mid, HI);//merging the left and right sequences } ...}
To sort an array with 15 elements as an example, its call stack is:
We take the merge step separately, and we can see that the merge process is as follows:
Three illustrations and animations
If you take the sort 38,27,43,3,9,82,10 as an example, you can see the merge sort as:
is the visualization of the merge sort:
The combined sort of 6 5 3 1 8 7 24 has the following animation effects:
Demonstrates the efficiency of a merge sort in different situations:
Quad Analysis
1. The average time complexity of the merge sort is O (NLGN)
Proof: The merge sort is the first time complexity that we have encountered is not N2 of the time complexity of NLGN (here LGN represents log2n) sorting algorithm, the following gives proof of the time complexity analysis of the merge sort:
Assuming that D (N) is the time taken to merge the entire sequence, a merge sort can be sorted by two two d (N/2), plus the time used to compare and calculate the middle number associated with N. The entire merge sort can be represented by the following recursive expressions:
D (N) =2d (N/2) +n,n>1;
D (N) =0,n=1; (When N=1, the array has only 1 elements, it is sequenced, and the time is 0)
Because recursion is often used in the divide-and-conquer algorithm, there is a chapter devoted to the solution and proof of recursion in CLRs, using the principal theorem (master theorem) to directly solve the recursive value, which I will briefly introduce later. Here is a simple list of two ways to prove that the recursive time complexity is O (NLGN):
Prof1: For convenience, we assume that the array n is an integer power of 2, so that we can draw a tree based on the recursive formula:
We can see that when we mergesort the array n, it is a stepwise division, so that the formation of a full two fork tree, each tree and child nodes are N, the depth of the tree is the number of layers lgn+1, the depth of two fork tree can be consulted related data, the last layer of sub-nodes did not draw out. So, this tree has lgn+1 layers, each layer has n nodes, so
D (N) = (lgn+1) n=nlgn+n=nlgn
Prof2: When we are solving recursive expressions, there is also a common method is the mathematical induction method,
First, based on the initial values of our recursive expressions and observations, we suspect D (N) =nlgn.
- When N=1, D (1) = 0, satisfies the initial conditions.
- For ease of derivation, assume that n is an integer power n=2k of 2, i.e. D (2k) =2klg2k = k*2k
- In the case of N+1 D (n+1) =d (2k+1) =2k+1lg2k+1= (k+1) * 2k+1, so assumed, D (N) =nlgn.
2. Merge sort requires additional auxiliary space of length N to complete sorting
If you sort a sequence of length n requires extra space for <=clogn, it is considered to be an in-place sort (in places sort), which is a small, fixed amount of additional secondary memory space required to complete the sort operation. Previously learned selection sort, insert sort, and hill sort are all sorted in situ.
But in the merge sort, we're going to create a secondary sorted array of size n to hold the initial array or to hold the merged array, so we need an extra space of auxiliary length of N. There are, of course, predecessors who have transformed the merge order to merge in place, but the implementation of the algorithm becomes more complex.
Requiring additional n of space to aid in sorting is the biggest drawback of merging sorting, and other algorithms may be required in an environment where memory is more concerned.
Five Points of improvement
Some improvements to merge sorting can improve the efficiency of merge sorting.
1. When dividing into smaller sub-sequences, you can usually use Insert sort instead of merge sort
For smaller sub-sequences (usually the number of sequence elements is about 7), we can use the insertion sort to sort directly without continuing recursion), the algorithm is modified as follows:
Private Const int CUTOFF = 7;//with insertion sort threshold private static void sort (t[] array, int lo, int hi) { if (lo >= hi) return; If the subscript is greater than superscript, return if (Hi <= lo + CUTOFF-1) Sort<t>. Selectionsort (Array, lo, hi); int mid = lo + (hi-lo)/2;//array sort (Array, lo, mid),//loop to the left element sort (Array, mid + 1, HI);//loop sorts the right elements Merg E (Array, lo, Mid, HI);//merge the left and right sequences together}
2. If it's already sorted, you don't have to merge.
When the maximum value of the sequence on the left of the order is <= to the minimum value of the right sequence, the entire sequence is already sorted.
The algorithm changes as follows:
private static void Sort (t[] array, int lo, int hi) { if (lo >= hi) return;//if subscript is greater than superscript, return if (Hi <= lo + CUT OFF-1) Sort<t>. Selectionsort (Array, lo, hi); int mid = lo + (hi-lo)/2;//array sort (Array, lo, mid),//loop to the left element sort (Array, mid + 1, HI);//Loop sort on right element if (Array[mid]. CompareTo (Array[mid + 1]) <= 0) return; Merge (Array, lo, Mid, HI);//merging of left and right sequences}
3. parallelization
Divide-and-conquer algorithms are usually easier to parallelize, in the case of concurrency and parallelism This article has shown how to parallelize the quick sort (quick sort is explained in the next article), merging the sort, because we divide the left and right sides of the sequence is independent, so can be parallel, it is worth noting that Parallelization also has a threshold value, when the sequence length is less than a certain threshold, stop parallelization can improve efficiency, these detailed discussion in the concurrency and parallelism This article has detailed introduction, here no longer repeat.
Six uses
Merge sort and quick sort are all algorithms with time complexity NLGN, but compared to quick sort, merge sort is a sort of stability, that is, two elements with the same sort key will not change relative position before and after the whole sequence is sorted. This feature makes the merge sort the most efficient of the stability sort. The reference objects are sorted in Java, and the internal implementation of the stability ordering of Perl, C + +, and Python is the combined sort used.
Vii. Conclusion
In this paper, we introduce a typical combined sorting algorithm in the divide-and-conquer algorithm, which is the first sort of time complexity for NLGN, and the analysis of the complexity of the algorithm, I hope this article will help you understand the merging sort, and the fast sorting algorithm is introduced below.
Discussion on algorithm and data structure