算法导论-排序(二)快速排序、随机化快速排序
目录
1、本文介绍
2、快速排序
3、随机化快速排序
4、完整源码
5、参考资料
内容
1、本文介绍
主要内容分为两部分,一部分是介绍快速排序算法,分析其在最好、最坏以及最好最差交替出现情况下的算法效率;另一部分则是介绍随机化快排算法,以及分析其算法复杂度。最后给出c++实现代码。
2、快速排序
快速排序也是基于分治思想,首先把要排序的数组分为两份,然后再分别对分好的子数组进行快速排序。当子数组为1个元素时递归介绍,排序完成。快速排序属于“原地排序”,就是说在原有的数组内存上排序、不占用其他内存。归并排序就不一样,它则需要额外的空间来进行归并排序操作。下图是快速排序的分治思想:
由上图可以知道,快速排序里面最关键的就是第一步:分化(Partition)的步骤,这是该算法处理问题的核心。所以我们可以把快排看成是递归地划分数组,就像归并排序是递归地合并数组一样。关于Paritition的具体算法,目前有好几种,其伪代码稍有不同但是它们的原理都是一样的。当然最重要的一点是分化(Partition)的算法复杂度都是线性的,也就是O(n)。下面是分化(Partition)的一种伪码:
1 Partition(A,p,q) 2 x <- A[p] //选第一个为主元 3 i <- p 4 for j <- p+1 to q 5 do if A[j] < x //小于主元的数据放在左边 6 then i <- i+1 7 exch A[i] <-> A[j] // 交换A[i] A[j] 8 exch A[p] <-> A[i] // 交换 9 return i //返回划分好后主元索引
分划好后就是简单的递归,下面是快排的伪代码:
1 QuickSort(A,p,q) 2 if p < q //不满足就介绍递归 3 then r <- Partition(A,p,q) //分划 4 QuickSort(A,p , r-1) //递归快排左子数组 5 QuickSort(A , r+1 ,q) //递归快排右子数组
接下来分析快排的算法效率
1)最坏情况下分析
当输入序列是正序或者是反序的时候,效率最坏,这时效率是Θ(n2)
2) 最优情况下分析
其他情况下的算法效率趋向于Θ(nlgn)
那么我们如何保证我们总是效率处于最优情况下的呢?这就是随机化快速排序需要解决的问题。
3、随机化快速排序
我们已经知道,若输入本身已被排序,那么对于快排来说就糟了。那么如何避免这样的情况?一种方法时随机排列序列中的元素;另一种方法时随机地选择主元(pivot)。这便是随机化快速排序的思想,这种快排的好处是:其运行时间不依赖于输入序列的顺序。
经分析, 随机化快排的算法效率是Θ(nlgn)。
实现:我们只需在选取主元时加入随机因素即可。其他与快排一样
下面是分化(Partition)编程(c++)实现
1 int Random_Partition(vector<T> &A,int p,int q) 2 { 3 int i=rand()%(q-p)+p; //此行与快排不同、加入随机数参数器 4 Swap(A[i],A[p]); //此行与快排不同、随机选取主元 5 return Partition(A,p,q);//此次与快速排序一样 6 }
// 随机化快速排序
1 void Random_Quick_Sort(vector<T> &A,int p,int q) 2 { 3 if (p<q) 4 { 5 int i=Random_Partition(A,p,q); 6 Random_Quick_Sort(A,p,i-1); 7 Random_Quick_Sort(A,i+1,q); 8 } 9 }
4、完整源码
下面源码包含之前讨论过的插入排序、归并排序算法。最后给出时间比较
CTimer.h (测试间的头文件实现,不懂无需纠结)
1 #ifndef CTIMER_HH 2 #define CTIMER_HH 3 class CTimer 4 { 5 public: 6 CTimer() 7 { 8 QueryPerformanceFrequency(&m_Frequency); 9 Start(); 10 } 11 void Start() 12 { 13 QueryPerformanceCounter(&m_StartCount); 14 } 15 double End() 16 { 17 LARGE_INTEGER CurrentCount; 18 QueryPerformanceCounter(&CurrentCount); 19 return double(CurrentCount.LowPart - m_StartCount.LowPart) *1000/ (double)m_Frequency.LowPart; 20 } 21 void ShowNow() 22 { 23 LARGE_INTEGER CurrentCount; 24 QueryPerformanceCounter(&CurrentCount); 25 cout<<"Timer Count is:"<<double(CurrentCount.LowPart - m_StartCount.LowPart)*1000 / (double)m_Frequency.LowPart<<endl; 26 } 27 private: 28 LARGE_INTEGER m_Frequency; 29 LARGE_INTEGER m_StartCount; 30 }; 31 #endif
Sort.h (排序算法[插入排序、归并排序、快速排序、随机化快速排序]实现头文件)
1 #ifndef SORT_HH 2 #define SORT_HH 3 template<typename T >//带模板 4 class Sort 5 { 6 public: 7 void insertion_sort(vector<T> &A);//插入排序 8 void merge_sort(vector<T> &A,int p,int r);//归并排序 9 void print_element(vector<T> A);//打印数组 10 void Quick_Sort(vector<T> &A,int p,int q);//快速排序 11 int Partition(vector<T> &A,int p,int q);//分划 12 void Swap(T &m,T &n);//交换数据 13 void Random_Quick_Sort(vector<T> &A,int p,int q);//随机化快速排序 14 int Random_Partition(vector<T> &A,int p,int q);//随机化分划 15 private: 16 void merge(vector<T> &A,int p,int q,int r);// 归并排序子程序 17 }; 18 template<typename T>//插入排序 19 void Sort<T>::insertion_sort(vector<T> &A) 20 { 21 int i,j; 22 T key; 23 int len=A.size(); 24 for (j=1;j<len;j++) 25 { 26 i=j-1; 27 key=A[j]; 28 while (i>=0&&A[i]>key) 29 { 30 A[i+1]=A[i]; 31 i--; 32 } 33 A[i+1]=key; 34 } 35 } 36 37 template<typename T>// 归并排序子程序 38 void Sort<T>::merge(vector<T> &A,int p,int q,int r) 39 { 40 int n1=q-p+1; 41 int n2=r-q; 42 T *L=new T[n1+1]; 43 T *R=new T[n2+1]; 44 45 for (int i=0;i<n1;i++) 46 L[i]=A[i+p]; 47 for (int i=0;i<n2;i++) 48 R[i]=A[i+q+1]; 49 50 L[n1]=R[n2]=INT_MAX; 51 52 int i=0,j=0; 53 for (int k=p;k<=r;k++) 54 { 55 if (L[i]>R[j]) 56 { 57 A[k]=R[j]; 58 j++; 59 } 60 else 61 { 62 A[k]=L[i]; 63 i++; 64 } 65 } 66 67 delete[] L; 68 delete[] R; 69 70 } 71 72 template<typename T>//归并排序 73 void Sort<T>::merge_sort(vector<T> &A,int p,int r) 74 { 75 if (p<r) 76 { 77 int mid=(p+r)/2; 78 merge_sort(A,p,mid); 79 merge_sort(A,mid+1,r); 80 merge(A,p,mid,r); 81 } 82 } 83 84 template<typename T>//交换数据 85 void Sort<T>::Swap(T &m,T &n) 86 { 87 T tmp; 88 tmp=m; 89 m=n; 90 n=tmp; 91 } 92 93 /***********快速排序分划程序*************/ 94 template<typename T> 95 int Sort<T>::Partition(vector<T> &A,int p,int q) 96 { 97 T x=A[p]; 98 int i=p; 99 for (int j=p+1;j<=q;j++) 100 { 101 if (A[j]<x) 102 { 103 i=i+1; 104 Swap(A[i],A[j]); 105 } 106 } 107 Swap(A[p],A[i]); 108 return i; 109 } 110 template<typename T>//快速排序 111 void Sort<T>::Quick_Sort(vector<T> &A,int p,int q) 112 { 113 if(p<q) 114 { 115 int i=Partition(A,p,q); 116 Quick_Sort(A,p,i-1); 117 Quick_Sort(A,i+1,q); 118 } 119 } 120 121 template<typename T>//随机化快速排序分划程序 122 int Sort<T>::Random_Partition(vector<T> &A,int p,int q) 123 { 124 int i=rand()%(q-p)+p; 125 Swap(A[i],A[p]); 126 return Partition(A,p,q); 127 } 128 129 template<typename T>//随机化快速排序 130 void Sort<T>::Random_Quick_Sort(vector<T> &A,int p,int q) 131 { 132 if (p<q) 133 { 134 int i=Random_Partition(A,p,q); 135 Random_Quick_Sort(A,p,i-1); 136 Random_Quick_Sort(A,i+1,q); 137 } 138 } 139 140 template<typename T>//打印数组 141 void Sort<T>::print_element(vector<T> A) 142 { 143 int len=A.size(); 144 for (int i=0;i<len;i++) 145 { 146 std::cout<<A[i]<<" "; 147 } 148 std::cout<<std::endl; 149 } 150 #endif
Sort_main.cpp (测试主程序)
1 #include <iostream> 2 #include <vector> 3 #include <time.h> 4 #include <Windows.h> 5 using namespace std; 6 #include "Sort.h" 7 #include "CTimer.h" 8 9 #define N 10 //排序数组大小 10 // 随机参数排序数组 11 void Random(vector<int> &a,int n) 12 { 13 int i=0; 14 srand( (unsigned)time( NULL ) ); 15 while(i<n) 16 { 17 a[i++]=rand(); 18 } 19 } 20 int main() 21 { 22 Sort<int> sort1; 23 CTimer t; 24 vector<int > vec_int(N); 25 Random(vec_int,N); 26 cout<<"源数组:"; 27 sort1.print_element(vec_int); 28 t.Start(); 29 sort1.Quick_Sort(vec_int,0,vec_int.size()-1); 30 cout<<"快速排序:"<<t.End()<<"ms"<<endl; 31 sort1.print_element(vec_int); 32 Random(vec_int,N); 33 t.Start(); 34 sort1.Random_Quick_Sort(vec_int,0,vec_int.size()-1); 35 cout<<"随机化快速排序:"<<t.End()<<"ms"<<endl; 36 sort1.print_element(vec_int); 37 Random(vec_int,N); 38 t.Start(); 39 sort1.insertion_sort(vec_int); 40 cout<<"插入排序:"<<t.End()<<"ms"<<endl; 41 sort1.print_element(vec_int); 42 Random(vec_int,N); 43 t.Start(); 44 sort1.merge_sort(vec_int,0,vec_int.size()-1); 45 cout<<"归并排序:"<<t.End()<<"ms"<<endl; 46 sort1.print_element(vec_int); 47 48 system("PAUSE"); 49 return 0; 50 }
输出:
排序算法时间比较:
数组大小 | 快速排序(ms) | 随机化快速排序(ms) | 插入排序(ms) | 归并排序(ms) |
10 | 0.0054523 | 0.00673552 | 0.00481085 |
0.0189227 |
100 | 0.108084 | 0.107763 | 0.377492 | 0.232845 |
1000 | 1.71427 | 1.47212 | 49.5864 | 2.67323 |
10000 | 34.795 | 19.2226 | 3542.74 | 30.3318 |
100000 | 232.691 | 233.02 | 352846 | 350.414 |
1000000 | 3032.3 | 3273.46 | ......(太大、没测) | 4017.02 |
自我小结:对随机产生的数组进行排序,1)可以发现插入排序没有优势、特别是数组比较大时耗时太多;2)快速排序、随机化快速排序、归并排序性能不错,然而两种快排比归并排序性能好点;3)当数据量变大时,可以看出性能排序为快速排序、随机化快速排序、归并排序、插入排序;4)由于这里的数组是由随机数产生的,没有显示出随机化快速排序的优势,但是当数组为已排序情况下随机化快排将比快排性能好。
5、参考资料
【1】http://blog.csdn.net/xyd0512/article/details/8259382
【2】http://blog.csdn.net/kenden23/article/details/14558231
【3】http://www.cnblogs.com/lidabo/archive/2013/01/08/2850418.html