快速排序,堆排序和归并排序谁更快?

时间复杂度:快速排序最坏情况只有两种,并且通过随机化算法可以避免,因此这三种算法时间复杂度可以说是一样的。

空间复杂度:快排O(logn),堆O(1),归并O(n)。

当n比较大的时候,归并排序往往会出内存溢出错误,如普通机器n>1000万时。

并且假如你能意识到cashe的存在,就能推出归并排序应该是比其他两个要慢的

关于普通快排和堆排的比较

自己写了一下代码,简单直接,没有经过什么特殊的优化,但代码也不跟其他人那样有很多没必要的东西。

import java.util.Arrays;
import java.util.Calendar;
import java.util.Random;




public class CmpQsHeap {


/**
* @param args
*/
public static void main(String[] args) {
long time =0;
int[] ary = genRandAry(1000000);
//int[] ary =new int[] {3,5,7,1,4,2,8,6,9};//实验数据,验证排序代码正确
int[] ary1 = Arrays.copyOf(ary, ary.length);//得到一个和生成的随机数组一样的数组。这样可以使两种排序使用同样的数据
int[] ary2 = Arrays.copyOf(ary, ary.length);

int low =0;
int high =ary.length-1;
long begin = Calendar.getInstance().getTimeInMillis();
Qs(ary,low,high);
long end = Calendar.getInstance().getTimeInMillis();
time = (end - begin);
// for(int i:ary){
// System.out.println(i);
// }
System.out.println(time);



time = 0;
begin = Calendar.getInstance().getTimeInMillis();
heapsort(ary1,ary1.length);
end = Calendar.getInstance().getTimeInMillis();
time = (end - begin);
// for(int i:ary1){
// System.out.println(i);
// }
System.out.println(time);



time = 0;
begin = Calendar.getInstance().getTimeInMillis();
Arrays.sort(ary2);
end = Calendar.getInstance().getTimeInMillis();
time = (end - begin);
// for(int i:ary1){
// System.out.println(i);
// }
System.out.println(time);

}

public static int[] genRandAry(int n) {
int[] ary = new int[n];
Random rand = new Random();
for (int i = 0; i < ary.length; i++) {
ary[i] = rand.nextInt();
}
return ary;
}


public static void Qs(int R[],int low, int high){
if(lowint p =Partition(R,low,high);
Qs(R,low,p-1);
Qs(R,p+1,high);
}
}

private static int Partition(int[] R, int low, int high) {
int pa =R[low];
while(lowwhile(low=pa) --high;
R[low] =R[high];
while(lowR[high] =R[low];
}
R[low] =pa;
return low;
}




public static void heapsort(int A[],int n) {
int i,k;
for (i=(n>>1)-1;i>= 0;i--)
shift(A,i,n);

for (i=n-1;i>=1;i--)
{
k=A[0];A[0]=A[i]; A[i]=k;
shift(A,0,i);
}
}




public static void shift(int A[] , int i , int n){
int k,t;
t=A[i]; k=(i<<1)+1;
while (k {
if ((k < n - 1) && (A[k] < A[k+1])) k++;
if (t>=A[k])
break;

A[i]=A[k];
i=k;
k=2*i+1;

}
A[i] = t;
}






}


输出

157
333
136

从上到下 依次是 普通快排,堆排,和Arrays.sort();

由结果可知普通快排所用的时间比堆排序要短将近一倍。

因为数据是随机生成的,我和你的机器可能不一样,这个数字在你跑的时候,可能会有所不同。


以下是百度中的一篇博客

http://hi.baidu.com/ycdoit/item/6b5f5b9571a843becc80e560

他提到了算法导论中说到的 对普通快排进行优化的方法也就是 Arrays.sort()中运用的。

1) 利用存储子任务的栈来消除递归

2) 利用基于三中值分区的中枢值

3) 设定一个使用切分时数组长度的最小值,如果小于这个值,就使用插入排序(这个最小值根据经验给定,一般设定为4或者5)

4) 当处理子数组的时候,首先将大的子数组压入栈中,这样可以最小化栈的总大小,确保小的问题首先被解决


但是他的结论是 堆排序比普通快排要好。(不知道是不是他的调用顺序问题,先用堆排,后用快排的话,快排就变为了O(n2))


由上述代码可知,堆排序的过程就是n次建堆的过程,

总比较次数小于2nlogn (以2为底),即比较次数比普通快排少。

建堆的代码已经相当简练,优化带来的效率提高有限。

因为不涉及到切分问题,所以能加快速度的 第3点只能用1次,而在快排中可以用多次。

堆排序是循环而非递归过程,不存在显式栈问题,而且这个循环调用函数引起了堆的变化,所以不可能提到循环外面。

循环展开和多路并发方面也不如 快排,(快排是适用分治法的,各个子问题相互独立)。

综上,快速排序最终肯定能完爆堆排序。



符: 归并

  1. private void merge(int[] a, int[] b, int[] ary) {
  2. int i = 0;
  3. int j = 0;
  4. int k = 0;
  5. while (i < a.length && j < b.length) {
  6. if (a[i] <= b[j]) {
  7. ary[k++] = a[i++];
  8. } else {
  9. ary[k++] = b[j++];
  10. }
  11. }
  12. for (; i < a.length; ++i) {
  13. ary[k++] = a[i];
  14. }
  15. for (; j < b.length; ++j) {
  16. ary[k++] = b[j];
  17. }
  18. }





你可能感兴趣的:(快速排序,堆排序和归并排序谁更快?)