快速排序,堆排序和归并排序谁更快?
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];
}
}
时间复杂度:快速排序最坏情况只有两种,并且通过随机化算法可以避免,因此这三种算法时间复杂度可以说是一样的。
空间复杂度:快排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];
}
}
相关文章推荐
- 算法导论之插入排序,选择排序,归并排序,冒泡排序,希尔排序,堆排序,快速排序的c语言实现
- 快速排序、希尔排序、插入排序、选择排序、归并排序、堆排序总结
- 排序----插入排序,二路归并排序,堆排序,快速排序
- 关于堆排序、归并排序、快速排序的比较,到底谁快
- 快速排序、希尔排序、插入排序、选择排序、归并排序、堆排序总结
- 对字符串进行直接插入排序、堆排序、归并排序、快速排序实现以及性能分析
- 初级版、正宗版、升级版冒泡排序;简单选择排序;直接插入排序;希尔排序;堆排序;递归法 、非递归法归并排序;快速排序; 快速排序优化算法
- 算法导论之插入排序,选择排序,归并排序,冒泡排序,希尔排序,堆排序,快速排序的c语言实现
- 排序算法java版,速度排行:冒泡排序、简单选择排序、直接插入排序、折半插入排序、希尔排序、堆排序、归并排序、快速排序
- 快速排序、归并排序、堆排序的实现
- C++ 实现堆排序 归并排序 快速排序
- 快速排序、希尔排序、插入排序、选择排序、归并排序、堆排序总结
- 快速排序,堆排序和归并排序谁更快?
- 排序(希尔排序,堆排序,归并排序,快速排序)
- [C] - 希尔排序,非递归的快速排序,递归的快速排序,堆排序,归并排序,基数排序
- 排序算法java版,速度排行:冒泡排序、简单选择排序、直接插入排序、折半插入排序、希尔排序、堆排序、归并排序、快速排序
- 程序员必知的8大排序(①直接插入排序②希尔排序③简单选择排序④堆排序⑤冒泡排序⑥快速排序⑦归并排序⑧基数排序)
- 数据结构与算法:七种排序算法总结(冒泡排序、选择排序、直接插入排序、希尔排序、堆排序、归并排序、快速排序)
- 排序3——堆排序,归并排序,快速排序
- 插入排序、选择排序、归并排序、堆排序、快速排序的JAVA实现