您的位置:首页 > 其它

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

2013-09-12 16:32 357 查看
转自:/article/8658493.html

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

空间复杂度:快排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
;

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次,而在快排中可以用多次。

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

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

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

符: 归并

private void merge(int[] a,
int[] b, int[] ary) {

int i = 0;

int j = 0;

int k = 0;

while (i < a.length && j < b.length) {

if (a[i] <= b[j]) {
ary[k++] = a[i++];
} else {
ary[k++] = b[j++];
}
}

for (; i < a.length; ++i) {

ary[k++] = a[i];
}
for (; j < b.length; ++j) {

ary[k++] = b[j];
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐