转自:http://blog.csdn.net/bingjing12345/article/details/7827419
时间复杂度:快速排序最坏情况只有两种,并且通过随机化算法可以避免,因此这三种算法时间复杂度可以说是一样的。
空间复杂度:快排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次,而在快排中可以用多次。
堆排序是循环而非递归过程,不存在显式栈问题,而且这个循环调用函数引起了堆的变化,所以不可能提到循环外面。
循环展开和多路并发方面也不如 快排,(快排是适用分治法的,各个子问题相互独立)。
综上,快速排序最终肯定能完爆堆排序。
符: 归并