16th-Summary of Introduction to algorithms-development of practical quicksort Algorithms

Source: Internet
Author: User

The answer is half ninety.

After reading the quick sorting, I understand its idea and will implement its Code. However, is this the quicksort algorithm I need? Apparently not.

Question 7-2: What if the elements in the array are the same?

If a program is directly implemented using pseudo code, the time complexity is the square of n. The author provides an idea when a [p .. if the elements in R] are the same, the returned Q is (p + r)/2, which ensures the best performance.


Question 7-4-5: Is it best to perform quick ORT recursion? Can it be improved?

The author provides another idea: when the input array is almost ordered, the insertion sorting speed is very fast. In practice, when we call a fast sorting for a child array with a length of K, let it return directly; and finally run the insert sorting for the entire array;

Exercise 7-2: Can we implement a [p .. r] obtain a [p .. q-1] less than the principal component; A [Q... t] is equal to the principal component; A [t + 1 .. r] greater than the principal component?

Exercise 7-4: Eliminating tail recursion can improve program performance...


Exercise 7-5 Q: How to Select a principal component? If the random selection implementation is too slow and the effect is not necessarily good, but directly selecting the last element is too prone to the worst case.

The most common way to improve the RANDOM-QUICKSORT is to select the principal component more carefully from the sub-array rather than randomly. The common practice is to use three numbers in China and France:

Select three elements randomly from the sub-array and use the digits as the principal element;

Exercise 7-6: In fact, Interval Fuzzy sorting can also adopt the same sorting method as quicksort. How to Write a scalable quicksort algorithm?

There are so many details or tips hidden behind an algorithm. Although we write a simple question for the purposes, we do not consider so many questions, however, in practice, we naturally need to consider these details. So the purpose of this article is ------- how to implement a practical sorting algorithm?


Program Description: Based on STL. The naming rules are as follows: All functions that are called only in the function but not user-oriented interfaces are _ xxxx (except sort ..)

// Sort // insert sorting here is consistent with the pseudo code in introduction to algorithms // pair iterator [first, last) elements in the range are sorted incrementally. template <typename randomiterator> void _ insertion_sort (randomiterator first, randomiterator last) {typedef typename iterator <randomiterator >:: value_type; if (first = last) return; For (randomiterator I = first + 1; I! = Last; ++ I) {randomiterator J = I-1; value_type key = * I; while (j> = first & Key <* j) {* (J + 1) = * j; -- J;} * (J + 1) = key;} // pair iterator [first, last) elements in the range are sorted by function objects. <typename randomiterator, typename compare> void _ insertion_sort (randomiterator first, randomiterator last, compare comp) {typedef typename operator <randomiterator> :: value_type; If (first = last) return; For (randomiterator I = first + 1; I! = Last; ++ I) {randomiterator J = I-1; value_type key = * I; while (j> = first & Comp (Key, * j )) {* (J + 1) = * j; -- J;} * (J + 1) = key;} // else ;}}//-------------------------------------------------------------------------------------------------

The average complexity of quicksort is O (nlgn), but the worst time complexity of the release version we use here is O (nlgn)

// Policy 1: three-point median primary key/Three-point median template <typename T> inline const T & _ median (const T & A, const T & B, const T & C) {if (a <B) {If (B <c) // A <B <creturn B; else if (a <C) // A <B, A <C, B> = creturn C; else // A <B, B> = C, A> = creturn ;} else if (a <c) // A> B, A <creturn A; else if (B <C)/A> B, A> C, B <creturn C; else // A> B, A> C, B> creturn B ;}//------------------------------------------------------------------------

Then there is the division code of quicksort. Here, the Division implementation selects the pseudo code in the introduction to algorithms, rather than the implementation method of Exercise 7-1 (STL uses the implementation method of Exercise 7-1)

// Functions // two auxiliary tool functions are used to exchange the value template <typename randomiterator> inline void iter_swap (randomiterator & I, randomiterator & J) of the elements referred to by the two iterators) {swap (* I, * j);} template <typename T> inline void swap (T & L, T & R) {T TMP = r; r = L; L = TMP ;}//--------------------------------------------------------------------------

// Partition // Partition Function, divided into two sections: [first, I) smaller than or equal to limit [I + 1, last) greater than or equal to limit ttemplate <typename randomiterator, typename T> randomiterator _ partition (randomiterator first, randomiterator last, t break) {for (randomiterator I = first; I! = Last; ++ I) {If (* I = random) {randomiterator TMP = last-1; iter_swap (I, TMP); break ;}} randomiterator I = first-1; randomiterator r = last-1; for (randomiterator J = first; J! = R; ++ J) {If (* j <= callback) {++ I; iter_swap (I, j) ;}++ I; iter_swap (I, r); return I;} // The input function object is divided here. // If the input is greater than the number, the Child array on the left is greater than the limit, <typename randomiterator, typename T, typename compare> randomiterator _ partition (randomiterator first, last, t second, compare comp) {for (randomiterator I = first; I! = Last; ++ I) {If (* I = random) {randomiterator TMP = last-1; iter_swap (I, TMP); break ;}} randomiterator I = first-1; randomiterator r = last-1; for (randomiterator J = first; J! = R; ++ J) {If (COMP (* j, done) {++ I; iter_swap (I, j) ;}++ I; iter_swap (I, r); return I ;}

The general program comes:

// Sort // policy 2: used in combination with insert sorting, for example, Exercise 7-4-5 // Policy 3: Remove tail recursion // policy 4: Hybrid sorting: Introspection sorting; when the split behavior deteriorates to N ^ 2 behavior, it can detect itself and use heap sorting to maintain the efficiency at nlgnconst int Threshold = 16; // when the number of sub-array elements is smaller than this value, the system directly returns the result by calling the quick sort method. // by default, the template is sorted in ascending order. <typename randomiterator> inline void sort (randomiterator first, randomiterator last) {If (first! = Last) {// The maximum number of layers for controlling recursive calls is _ lg (last-first) * Layer 2 _ intro_sort (first, last, value_type (first ), __lg (last-first) * 2); _ insertion_sort (first, last) ;}// sort the template by the input function object <typename randomiterator, typename compare> inline void sort (randomiterator first, randomiterator last, compare comp) {If (first! = Last) {_ intro_sort (first, last, value_type (first) ,__ lg (last-first) * 2, comp); _ insertion_sort (first, last, comp) ;}}// response );}}//---------------------------------------------------------------------------------------

// Define // _ LG is used to control the deterioration of the split // find the maximum K value of 2 ^ k <= N // In the sort program we pass in _ lg (last-first) * 2, meaning that if the element is 40, K = 5; the number of layers is a maximum of 10-layer template <typename size> inline size _ lg (size N) {size K; for (k = 0; n> 1; n> = 1) ++ K; return K;} // returns ;}//--------------------------------------------------------------------------------------

// Sort // template in ascending order <typename randomiterator, typename T, typename size> void _ intro_sort (randomiterator first, randomiterator last, T *, size depth_limit) {While (last-first> threshold) {If (depth_limit = 0) {// deterioration of splitting _ heap_sort (first, last); Return ;}-- depth_limit; // select randomiterator cut = _ partition (first, last, T (_ median (* First, * (first + (last-first)/2) using three-point mediator cut = _ partition ), * (last-1); // eliminates tail recursion _ intro_sort (cut + 1, last, value_type (first), depth_limit); last = cut ;}} // sort template by function object <typename randomiterator, typename T, typename size, typename compare> void _ intro_sort (randomiterator first, randomiterator last, T *, size limit, compare comp) {While (last-first> threshold) {If (depth_limit = 0) {// deterioration of splitting _ heap_sort (first, last, comp); Return ;}-- depth_limit; // select randomiterator cut = _ partition (first, last, T (_ median (* First, * (first + (last-first)/2) using three-point mediator cut = _ partition ), * (last-1), comp); // remove tail recursion _ intro_sort (cut + 1, last, value_type (first), depth_limit, comp ); last = cut ;}// else ;}}//--------------------------------------------------------------------------------------------

Currently, the main program outlines are basically implemented, but heap sorting is a big part. Here we still use the pseudo code "Introduction to algorithms:

// Configure // helper template <typename size> inline size _ parent (size I) {return (I-1)> 1 ;} template <typename size> inline size _ left (size I) {return (I <1) + 1;} template <typename size> inline size _ Right (size I) {return (I <1) + 2 ;}

The following functions have been implemented in Chapter 6 of Introduction to algorithms. You can refer to the code I wrote earlier;

// Heap // preserve the heap nature. The default maximum heap is template <typename randomiterator, typename distance> void _ heapify (randomiterator first, randomiterator I, distance Len) {distance I _index = I-first; distance largest = I _index; distance current_index = I _index; while (current_index <Len) {distance I _left_child = _ left (current_index); distance I _right_child = _ Right (Current_index); If (I _left_child <Len & (* (first + I _left_child)> * (first + current_index) {largest = I _left_child ;} if (I _right_child <Len & (* (first + I _right_child)> * (first + largest) {largest = I _right_child;} If (largest! = Current_index) {randomiterator tmp1 = first + current_index, tmp2 = first + largest; iter_swap (tmp1, tmp2); current_index = largest;} else {break ;}}} // maintain the heap nature. The minimum heap template <typename randomiterator, typename distance, typename compare> void _ heapify (randomiterator first, randomiterator I, distance Len, compare comp) {distance I _index = I-first; distance compest = I _index; distance current_index = I _index; while (cur Pai_index <Len) {distance I _left_child = _ left (current_index); distance I _right_child = _ Right (current_index); If (I _left_child <Len & Comp (* (first + I _left_child ), * (first + current_index) {compest = I _left_child;} If (I _right_child <Len & Comp (* (first + I _right_child), * (first + compest ))) {compest = I _right_child;} If (compest! = Current_index) {randomiterator tmp1 = first + current_index, tmp2 = first + compest; iter_swap (tmp1, tmp2); current_index = compest;} else {break ;}}// second ;}}}//------------------------------------------------------------------------------------------------

// Configure // create a heap for [first, last). The default value is the maximum heap template <typename randomiterator> inline void build_heap (randomiterator first, randomiterator last) {typedef iterator_traits <randomi:: difference_type; typedef iterator_traits <randomiterator>: value_type; If (last-first <2) return; // If the length is 0 or 1, do not rearrange difference_type Len = last-first; For (randomiterator it = first + (LEN/2)-1; it! = First-1; -- It) {_ heapify (first, it, Len) ;}// create a heap and create a template based on the function object <typename randomiterator, typename compare> inline void build_heap (randomiterator first, randomiterator last, compare comp) {typedef comment <randomiterator >:: value_type; if (last-first <2) return; // If the length is 0 or 1, you do not have to rearrange difference_type Len = last-first; For (randomite Rator it = first + (LEN/2)-1; it! = First-1; -- It) {_ heapify (first, it, Len, comp) ;}// response );}}//----------------------------------------------------------------------------------------

// Sort // heap sorting. The maximum heap is created by default. The template is sorted in ascending order. <typename randomiterator> void _ heap_sort (randomiterator first, randomiterator last) {typedef iterator_traits <rando :: difference_type; difference_type Len = last-first; build_heap (first, last); For (difference_type I = len-1; I> = 1; -- I) {randomiterator TMP = (first + len-1); iter_swap (first, TMP); -- Len; _ heapify (first, first, Len );}}

The following lists the heap sorting by function object versions. However, there are some details here. First, for the ascending sorting, we pass in the minor signs, but we need to create the largest heap, therefore, during the heap creation process, we need a greater number. Here we need a function object's reverse function to reverse the function object (the adapter ), we need to establish an adapter based on the conventions that everyone must follow. That is, when defining binary function objects, we should inherit the basic binary_function:

// Helper Program template <typename arg1, typename arg2, typename result> struct binary_function {typedef arg1 progress; typedef arg2 progress; typedef result result_type;}; Template <typename predicate> class binary_negate: public binary_function <typename predicate: first_argument_type, typename predicate: second_argument_type, bool> {protected: Predicate _ Pred; public: explicit binary_negate (const P Redicate & X): _ PRED (x) {} bool operator () (const typename predicate: first_argument_type & X, const typename predicate: second_argument_type & Y) const {return! _ PRED (x, y );}};

In this way, binary_negate <compare> (COMP) can be used to reverse function objects.

// Perform heap sorting by function object. template <typename randomiterator, typename compare> void _ heap_sort (randomiterator first, randomiterator last, compare comp) {typedef operator <randomiterator >:: too large; difference_type Len = last-first; build_heap (first, last, binary_negate <compare> (COMP); For (difference_type I = len-1; I> = 1; -- I) {randomiterator TMP = (first + len-1); iter_swap (first, TMP); -- Len; _ heapify (first, first, Len, binary_negate <compare> (COMP ));}}

Okay, so our program is finished. This is the fast sorting algorithm used!

Below are some tests:

Header files to be included:

#include<iostream>#include<cstdlib>

// Sort int main () {int A [10] in ascending order; For (INT I = 0; I <10; ++ I) {A [I] = rand () % 100;} Sort (A, A + 10); For (int * It = A; it! = A + 10; ++ it) {STD: cout <* It <"";} Char c = getchar ();}

Result:

First, define a function object greater than the number:

template<typename T>struct greater :public binary_function<T,T,bool>{bool operator()(const T& x,const T& y)const{return x>y;}};

Then sort them in descending order:


// Sort int main () {int A [10] in descending order; For (INT I = 0; I <10; ++ I) {A [I] = rand () % 69;} Sort (A, A + 10, greater <int> (); For (int * It = A; it! = A + 10; ++ it) {STD: cout <* It <"";} Char c = getchar ();}

Because the code I wrote is not very good, at least four Relational operators must be defined for a class that can be sorted:

struct student{int id;char name;student(int a=0,char b=0):id(a),name(b){ }bool operator<(const student& r)const{return id<r.id;}bool operator<=(const student& r)const {return id<=r.id;}bool operator==(const student& r)const {return id==r.id;}bool operator>(const student& r)const {return id>r.id;}};

This is just for testing, so the student class design is very rough, just to prove the correctness, the test program is also poorly written .... Understanding:


int main(){student A[10];for(int i=0;i<10;++i){int x=rand()%120;A[i].id=x;A[i].name=x+'a';}sort(A,A+10); for(student* it=A;it!=A+10;++it){   std::cout<<(*it).id<<" ";} char c=getchar();}

The last one is a problem I encountered in Chapter 6. for very large satellite data, for priority queues, we use handles (pointers) instead of directly using data, therefore, we also need to allow a handle or pointer pointing to a type of data to work in the priority queue. I will use heap_sort to explain this, my solutions to this problem .. I think this problem should be transferred to the handle design rather than the priority queue design.

If student is a very large data type, what I handle in the priority queue is the handle pointing to student. How can I make this handle work, the answer to this question is the same as how to make the student handle work in the heap sorting algorithm.

I defined the handle pointing to student-iterator. I declare again that the iterator design is very bad, just to test correctness ....

struct student_iter{student* p;student_iter():p(0){}student_iter(student* x):p(x){ }student_iter(const student_iter& x):p(x.p){ }student_iter& operator=(student_iter& x){p=(x.p);return *this;}bool operator<(const student_iter& r)const{return p->id<(r.p)->id;}bool operator<=(const student_iter& r)const {return p->id<=(r.p)->id;}bool operator==(const student_iter& r)const {return p->id==(r.p)->id;}bool operator>(const student_iter& r)const {return p->id>(r.p)->id;}int& operator*() const {return (*p).id;}};

After defining this, you can work:

int main(){student A[10];for(int i=0;i<10;++i){int x=rand()%124;A[i].id=x;A[i].name=x+'a';}student_iter B[10];for(int i=0;i<10;++i){B[i]=student_iter(&A[i]);} for(student* it=A;it!=A+10;++it){   std::cout<<(*it).id<<" ";} std::cout<<'\n';    _heap_sort(B,B+10);   for(student_iter* it=B;it!=B+10;++it){   std::cout<<*(*it)<<" ";}char c=getchar();}

For the extension of this program, for example, if there is a sequence of a large number of repeated elements in the sorting, we can design the algorithm according to "Introduction to algorithms" 7-2:

// When there is one more bool parameter, you can select a three-segment template <typename randomiterator, typename T> iter_pair <randomiterator> _ partition (randomiterator first, randomiterator last, t break, bool) {typedef iterator_traits <randomiterator >:: difference_type size_type; For (randomiterator I = first; I! = Last; ++ I) {If (* I = random) {randomiterator TMP = last-1; iter_swap (I, TMP); break ;}} randomiterator I = first-1; randomiterator r = last-1; size_type COUNT = 1; // equals to the number of worker elements. The initial value is 1 randomiterator TMP = r; // [TMP, the value of the element in last) is equal to limit tfor (randomiterator J = first; J! = TMP; ++ J) {If (* j <strong) {++ I; iter_swap (I, j);} else if (* j = strong) {// if the two are equal, Exchange (R-count) and J. In this case, the value of J is unknown, and you need to re-determine // The Cause -- J, increase the number of principal parts by TMP = r-count; iter_swap (J, TMP); ++ count; -- J ;}} size_type count _ = count; // [I + 1, the value of the element in I + Count + 1) is greater than limit. The value of the element in [I + Count + 1, last) is equal to that of limit TIF (ptrdiff_t (R-(I-count )) <count) {// at this time, the number of elements equal to or greater than the limit value is greater than that of the limit value. Count _ = ptrdiff_t (R-(I-count ));} // exchange for (Int J = 0; j <count _; ++ J) {randomiterator L = I + 1 + J, r = r-J; iter_swap (L, r);} // at this time, [first, I] is less than half, [I + 1, I + Count] is equal to half, [I + Count + 1, r] greater than limit treturn iter_pair <randomiterator> (I + 1, I + count );}

Then, design the sort function corresponding to one more bool type parameter for wide expansion. This will not be written here...


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.