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

来源:互联网 发布:网络歌手比较悲伤的 编辑:程序博客网 时间:2024/05/21 06:38

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

空间复杂度:快排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(low<high){
int 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(low<high){
while(low<high&&R[high]>=pa) --high;
R[low] =R[high];
while(low<high&&R[low]<=pa) ++low;
R[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<n)
   {
       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. }
0 0