排序算法3——快速排序
算法复杂度:O(nlogn)
原理
对于一组给定的记录,通过一趟排序后,将原序列分为两部分,其中一部分的所有记录均比后一部分的所有记录小,然后再依次对前后两部分的记录进行快速排序,递归该过程,直到序列中的所有记录均有序为止。
程序
1 public class quicksort { 2 3 public static void quickSort(int array[],int low,int high){ 4 int pivot; 5 if(low<high){ 6 pivot = partition(array,low,high); 7 quickSort(array,low,pivot-1); 8 quickSort(array,pivot+1,high); 9 } 10 } 11 12 13 public static int partition(int array[],int low,int high){ 14 int index = array[low]; 15 while(low<high){ 16 while(low<high&&array[high]>=index) 17 high--; 18 array[low] = array[high]; 19 while(low<high&&array[low]<index) 20 low++; 21 array[high] = array[low]; 22 } 23 array[low] = index; 24 return low; 25 } 26 27 28 public static void main(String[] args){ 29 int a[] = {5,4,9,8,7,6,0,1,3,2}; 30 int len = a.length; 31 quickSort(a,0,len-1); 32 for(int i=0;i<len;i++){ 33 System.out.println(a[i]+" "); 34 } 35 } 36 37 }
优化
1.优化选取枢轴
采取三数取中法。取三个关键字先进性排序,将中间数作为枢轴,一般是取左端、右端和中间三个数,也可以随机选取。这样至少这个中间数一定不会是最小或最大的数,从概率来说,取三个数均为最小或最大数的可能性微乎其微,因此中间数位于较为中间的值的肯能行就大大提高了。
将上面程序中的第14行改为如下程序
1 int index; 2 int m = low + (high - low) / 2; //中间元素下标 3 if(array[low]>array[high]) 4 swap(array,low,high); //交换左端与右端数据,保证左端较小。 5 if(array[m]>array[high]) 6 swap(array,m,high); //交换中间与右端数据,保证中间较小。 7 if(array[m]>array[low]) 8 swap(array,low,m); //交换中间与左端数据,保证左端较小。 9 /*此时array[low]已经是整个序列左中右三个关键字中的中间值。*/ 10 index = array[low];
2.优化小数组时的排序方案
当数组非常小时,快速排序反而不如直接插入排序来得更好。我们可以设置一个数组长度阈值(有资料认为7比较合适,也有认为50更合适,实际应用可适当调整),当数组长度在设定阈值之下时,就用直接插入排序,否则用快速排序。
3.优化递归操作
我们知道递归对性能是有一定的影响的,quickSort方法在其尾部有两次递归操作,如果待排序的序列划分极不平衡,递归深度将趋于n,这就不仅仅是速度快慢的问题了。栈的大小是很有限的,每次递归都会耗费一定的栈空间,函数的参数越多,每次递归耗费的空间也越多。如果能减少递归,将会大大提高性能。
我们采取尾递归优化。
将程序中的5-9行改为如下程序
1 while(low<high){ 2 pivot = partition(array,low,high); 3 quickSort(array,low,pivot-1); 4 low = pivot+1; 5 }
常见排序算法的特点和比较
排序法 | 平均时间 | 最好情形 | 最差情形 | 稳定度 | 额外空间 | 备注 |
冒泡 | O(n2) | O(n) | O(n2) | 稳定 | O(1) | n小时较好 |
选择 | O(n2) | O(n2) | O(n2) | 不稳定 | O(1) | n小时较好 |
插入 | O(n2) | O(n) | O(n2) | 稳定 | O(1) | 大部分已排序时较好 |
Shell | O(nlogn) | O(n1.3) | O(ns) 1<s<2 | 不稳定 | O(1) | s是所选分组 |
堆 | O(nlogn) | O(nlogn) | O(nlogn) | 不稳定 | O(1) | n大时较好 |
归并 | O(nlogn) | O(nlogn) | O(nlogn) | 稳定 | O(n) | n大时较好 |
快速 | O(nlogn) | O(nlogn) | O(n2) | 不稳定 | O(nlogn) | n大时较好 |