编程珠玑——快速排序总结
2015-11-07 21:35
246 查看
快速排序
快速排序是二十世纪最伟大的10大算法之一。如何写好一个快速排序通常不是一件容易的事,同时面试中或者实际应用中也经常要用到快速排序。编程珠玑第11章专门介绍了快速排序,涉及到了基本的算法思想实现和一些优化,这里进行简单的总结以便以后快速的回忆。
基本思想
快速排序用到了分治,它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。我们直观上可以想到式子:T(n)=2T(n/2)+O(n),从而得到时间复杂度为O(nlgn),好于O(n2)的时间复杂度。
实现
至于具体怎么分割数组,《编程珠玑》首先提到了一种方法,一趟快速排序流程如下:设置两个变量l、y,[l,r]表示要排序的数组范围,排序开始的时候:l=0,r=N-1;
以范围内的第一个数组元素作为关键比较数据,赋值给pivot,即pivot=A[l];
设index=l, 从i=l+1开始搜索,即由左开始向右搜索(i++),若找到一个小于key的值A[i],将A[i]和A[++index]互换;
i遍历到范围[l,r]的最右边r之后,再将pivot元素A[l]与A[index]互换(index此时指向最后一个小于pivot的元素的位置)
整个过程也是比较教科书式的,为了直观一些,举个例子int A[]={3,4,5,1,2,6};那么整个排序过程如下
开始,l=0,r=5,pivot = A[0]=3;第一次分割之后数组如下:
分割之后,分为两块左边,l=0,r=1; 右边l=3,r=5. 两边进行第二次分割之后数组如下:
最后再将l=4,r=5的数组快进行partition排序,即完成了整个数组的排序。
实现代码如下:
void quicksort(int a[],int l,int r) { if(l>=r) return; int pivot = a[l]; int index = l; for(int i=l+1;i<=r;i++) { if(a[i]<pivot) swap(a[++index],a[i]); } swap(a[l],a[index]); quicksort(a,l,index-1); quicksort(a,index+1,r); }
特殊情况
上述讲了一般情况下的快排思想,平均时间复杂度为O(nlgn)。但是一些特殊情况的输入下仍然会导致时间复杂度为O(n2)。全是重复元素
比如数组 a={3,3,3,3,3,3}.此时可以看到,第一次我们将数组分成长度1和5的两块;之后将长度为5的数组块又分成长度1和长度4的数组块….没有达到分治的效果。此时时间复杂度为T(n)=T(n−1)+O(n),显然就是O(n2)的复杂度了。针对这样的情况,我们可以稍微改变快排的思路,每一趟的快排前两步和上述的快排一样
和上述一样;
和上述一样;
设i=l+1,j=r;
从i开始向后搜索,即由左开始向右搜索(i++),找到第一个大于等于key的值A[i];从j开始向前搜索,即由右开始向左搜索(j–),找到第一个小于等于key的值A[i];若i>j,终止此步,否则将A[j]和A[i]互换,重复此步骤直到终止;
交换元素A[l]与元素A[j]。
这样所有元素都相同的情况也能基本实现每次partition二分,此时时间复杂都也变成了O(NlgN)。
实现代码如下:
void quicksort(int a[],int l,int r) { if(l>=r) return; int pivot = a[l]; int i=l; int j=r+1; while(1) { do{i++;}while((i<=r)&&a[i]<pivot); while(a[--j]>pivot); if(i>j) break; swap(a[i],a[j]); } swap(a[l],a[j]); quicksort(a,l,j-1); quicksort(a,j+1,r); }
输入已经排好序
面对这种情况,上述已经优化的快排代码又会出现O(N2)的时间复杂度。因为它会首先围绕最小的元素划分,将数组大小分为1和N-1,然后是围绕第二小的元素划分,依此类推,还是需要O(N2)的时间。解决这种情况的方法很简单就是随机选择划分元素,通过把A[l]与A[l,r]中的一个随机项交换来实现这一点。只需要加两行代码:
int num=rand()%(r-l+1) + l; swap(a[l],a[num]);
这样期望的运行时间就为O(NlgN)啦。
优化
《编程珠玑》课后练习11.11提出了一种快排的小优化点,如下图:之前我们都没有单独考虑过重复的元素,若是将数组中和比较元素t相同的所有元素都放到中间位置,那么之后进行下一趟快排的时候就会多过滤掉一些元素,起到了优化的效果。实现代码如下:
void quciksort(int a[],int l,int r) { if(l>=r) return; int m=n=r+1; int pivot = a[l]; for(int i=r;i>=l;i--) { while(a[i]<pivot) i--; if(a[i]==pivot) swap(a[--m],a[i]); else{//a[i]>pivot swap(a[--n],a[i]); swap(a[--m],a[i]); } } quciksort(a,l,m-1); quciksort(a,n,r); }
相关文章推荐
- 在命令行用 sort 进行排序
- 快速排序
- 动易2006序列号破解算法公布
- 文件遍历排序函数
- C#选择排序法实例分析
- Ruby实现的矩阵连乘算法
- C#插入法排序算法实例分析
- C#实现Datatable排序的方法
- 超大数据量存储常用数据库分表分库算法总结
- SQLSERVER的排序问题结果不是想要的
- Windows Powershell排序和分组管道结果
- C#数据结构与算法揭秘二
- C#冒泡法排序算法实例分析
- C#快速排序算法实例分析
- C#通过IComparable实现ListT.sort()排序
- C#选择法排序实例分析
- 算法练习之从String.indexOf的模拟实现开始
- C#算法之关于大牛生小牛的问题
- SQL学习笔记四 聚合函数、排序方法