[Introduction to algorithms] sorting (III): deep analysis of fast sorting

Source: Internet
Author: User

5. Quick sorting


Quick sorting is the most common sorting algorithm, including qsort of C, sort of C ++ and Java, fast sorting is adopted (C ++ and Java sort are optimized, and other sorting algorithms are mixed ).

The worst condition of fast sorting is O (n ^ 2), but the average efficiency O (n lg n), and the hidden constant factor in this O (n lg n) is very small, fast sorting is the fastest sorting algorithm, not a waste of name. In addition, it is also sorted in place.

Quick sorting is based on the sub-governance mode:

 

Decomposition: Array a [p .. R is divided into two (possibly empty) Sub-arrays A [p .. q-1 and a [q + 1 .. r] To Make A [p .. each element in q-1 is less than or equal to a (Q), and less than or equal to a [q + 1 .. elements in R. The submark Q is also calculated by dividing rows.

Solution: Uses recursive call to quickly sort the sub-array a [p .. q-1] And a [q + 1. R.

Merge: Because two sub-arrays are sorted in place, merging them requires no operation: the entire array a [P.. R] is sorted.

The following process achieves quick sorting:

Array Division: partition (key, which sorts the child array a [P. R] in place)



C ++ implementation:

// * Chapter 7 quick sorting of algorithms * // # include <cstdio> # include <ctime> # include <cstdlib> void swap (Int & A, Int & B) {if (! = B) {A ^ = B; B ^ = A; A ^ = B ;}// if the two numbers are equal, bitwise operation is not performed. If the two numbers are equal, the result will be 0 int partition (int * a, int P, int R) {int X, I; X = A [R]; I = p-1; for (Int J = P; j <= R-1; ++ J) {if (a [J] <= x) {swap (A [++ I], A [J]) ;}} swap (A [++ I], a [R]); return I;} void quicksort (int * a, int P, int R) {If (P <r) {int q = partition (A, P, R); quicksort (A, P, q-1); quicksort (A, q + 1, r) ;}} int main () {int arr [12] = {, 7,-4}; quicksort (ARR ); for (INT I = 0; I <12; ++ I) printf ("% d", arr [I]); putchar ('\ n'); Return 0 ;}

Quick performance analysis:

When the data volume is small, it is about a small sequence of 10 or more elements. The advantage of fast sorting is not obvious, or even slower than insertion sorting. However, once there is a large amount of data, its advantages will be fully utilized.


For example, the sort function in C ++ STL gives full play to the advantages of fast sorting, and uses quicksort and piecewise recursive sorting when the data volume is large. Once the data size after segmentation is smaller than a threshold, to avoid excessive Extra Load Caused by recursive calling of quicksort, insert sorting is used instead. If the recursive hierarchy is too deep, heapsort is used ). Therefore, the performance of C ++'s "hybrid weapons" sort is certainly better than that of C's qsort.


The running time of the Quick Sort is related to the division of partition.

The worst case is that the input array is fully sorted, so the left and right areas of each division are n-1 and 0, respectively, and the efficiency is O (n ^ 2 ).

For the proportional division of other constants, even if the ratio of left and right is, the effect is as fast as that in the center (detailed analysis is provided in the Introduction to algorithms)

That is, the total running time of any type is O (n lg n) according to the constant ratio ).


Randomization version of the Quick Sort:

This is because the Division produced by partition in the fast sorting may be "poor", and the key to Division is the choice of principal component A [R. We can use a differentRandom SamplingThe randomization technology of the principal component A [R] And a [p .. r] randomly selects an element exchange, which is equivalent to, our principal component is not necessarily the last a [R], but randomly from P ,..., r: Random Sampling in this range. In this way, we expect that the division of partition can be symmetric under the average condition.


Implementation of pseudocode:



C ++ implementation:

Int random (int m, int N) {srand (unsigned) Time (null); // contains the header file <cstdlib> return m + (RAND () % (n-m + 1);} int random_partition (int * a, int P, int R) {int I = random (P, R ); swap (A [R], a [I]); Return partition (A, P, R);} void random_quicksort (int * a, int P, int R) {If (P <r) {int q = random_partition (A, P, R); random_quicksort (A, P, q-1); random_quicksort (A, q + 1, r );}}

Further Optimization of Quick Sort:

1. tail recursion:

Traditional recursive algorithms are often seen as a flood of animals. its reputation seems to be always associated with inefficiency. tail recursion is extremely important and does not require tail recursion. The Stack Consumption of functions is immeasurable and the stacks of many intermediate functions need to be saved.

Stack depth in the quick rank:

The quicksort algorithm contains two recursive calls to itself. After partition is called, the Child arrays on the left and the Child arrays on the right are recursively ordered. The second recursive call in quicksort is not necessary. It can be replaced by an iterative control structure. This technology is called "tail recursion". Most compilers also use this technology.

The following version simulates tail recursion:

Quicksort '(A, P, R)

1 While P <r

2 do partition and sort left subarray.

3 Q partition (A, P, R)

4 quicksort '(A, p, q-1)

5 p then q + 1

Note that the first line is while rather than if

However, in the worst case, the recursive depth of this version is O (n) When division is poor, and the stack depth can be further optimized to O (LG
N?

Using the binary method, to make the stack depth into lgn in the worst case, we must make the child array on the left half of the original array after partition, in this way, the maximum recursive depth is lgn ).

One possible algorithm is to first obtain the median of (A, P, R) as the pivot element of partition, so that the number of elements on both sides of the left and right can be balanced as much as possible.

Because the time complexity of median is round (n), the expected time complexity O (nlgn) of the algorithm remains unchanged.

Optimized version of the tail recursion quick line:

2. "three numbers in the middle" Division

The so-called "three-digit fetch" means that three elements are randomly selected from the sub-array and the number of elements is used as the principal component. This is an upgraded version of the previous randomization version. Although it is an upgraded version, it can only affect the constant factor of the fast sorting time complexity O (nlgn.


The final quick typographical version of "optimized tail recursion" + "three numbers in" is given below:

// Optimize tail recursion + three-digit fetch medium-version quick sorting # include <cstdio> # include <ctime> # include <cstdlib> void swap (Int & A, Int & B) {if (! = B) {A ^ = B; B ^ = A; A ^ = B ;}// if the two numbers are equal, bitwise operation is not performed. If the two numbers are equal, the result will be 0 int partition (int * a, int P, int R) {int X, I; X = A [R]; I = p-1; for (Int J = P; j <= R-1; ++ J) {if (a [J] <= x) {swap (A [++ I], A [J]) ;}} swap (A [++ I], a [R]); return I;} inline int random (INT M, int N) {srand (unsigned) Time (null); Return m + (RAND () % (n-m + 1 ));} // The Inline int midnum (int A, int B, int c) {If (C <B) Swap (C, b); If (B <A) Swap (B, A); // after the two exchanges, a becomes the return B with the smallest number of three <C? B: C;} int threeone_partition (int * a, int P, int R) {int I, J, K, mid; // select three random numbers I = random (p, r); j = random (P, R); k = random (P, R); // retrieve "intermediate number" mid = midnum (A [I], A [J], a [k]); // exchange the intermediate number with a [R] If (A [I] = mid) swap (A [I], a [R]); else if (a [J] = mid) Swap (A [J], a [R]); else if (a [k] = mid) Swap (A [K], a [R]); Return partition (A, P, R );} void final_quicksort (int * a, int P, int R) {While (P <r) {int q = threeone_partition (A, P, R ); if (Q-P <r-q) {final_quicksort (A, P, q-1); P = q + 1 ;}else {final_quicksort (A, q + 1, R ); R = q-1; }}int main () {int arr [12] = {, 7,-4}; final_quicksort (ARR, 0, 11); For (INT I = 0; I <12; ++ I) printf ("% d", arr [I]); putchar ('\ n '); return 0 ;}

In addition, the quick schedule can be optimized as follows:

Non-recursive method: Simulate recursion to completely eliminate recursive calls.

Three-way fast sorting: the basic idea is to use v = A [R] as the benchmark in the division phase, and sort the array a [p .. R is divided into three sections: Left, middle, and right. A [p, J ],

A [J + 1 .. q-1], a [Q. R], where the left segment array element value is less than V, interrupt array is equal to V, segment array element is greater than v. Then, the algorithm is right and right

Recursive sorting of two arrays. This method greatly improves the sorting efficiency of arrays with a large number of identical data, even if there are not a large number of identical elements

This reduces the efficiency of the original fast sorting algorithm.


The above two types will have the opportunity to implement the code again later.


The summary of the Quick Sort ends here.

At present, we have summarized the five major sorting types, all of which are based onComparisonThe sorting speed is only O (n lg n ). Is it faster?

Next SummaryLinear time sortingThe speed will break through this bottleneck.



I am just a cainiao. If you make a mistake, I hope to point it out. Thank you.



-- The meaning of life is to give it meaning.

Original Http://blog.csdn.net/shuangde800 ,
D_double



Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.