• 关于堆排序、归并排序、快速排序的比较


    时间复杂度:

                堆排序      归并排序        快速排序
    最坏时间   O(nlogn)     O(nlogn)        O(n^2)
    最好时间   O(nlogn)     O(nlogn)        O(nlogn)
    平均时间   O(nlogn)     O(nlogn)        O(nlogn)

    辅助空间     O(1)         O(n)        O(logn)~O(n)

    从时间复杂度看堆排序最好

    有人说代码实现后,数据量足够大的时候,快速排序的时间确实是比堆排序短

    解释是,对于数组,快速排序每下一次寻址都是紧挨当前地址的,而堆排序的下一次寻址和当前地址的距离比较长。

    网友解答:

    1#

    4种非平方级的排序:
    希尔排序,堆排序,归并排序,快速排序

    我测试的平均排序时间:数据是随机整数,时间单位是秒
    数据规模    快速排序    归并排序    希尔排序    堆排序
    1000万       0.75           1.22          1.77          3.57
    5000万       3.78           6.29          9.48         26.54  
    1亿             7.65          13.06        18.79        61.31

    堆排序是最差的。
    这是算法硬伤,没办法的。因为每次取一个最大值和堆底部的数据(记为X)交换,重新筛选堆,把堆顶的X调整到位,有很大可能是依旧调整到堆的底部(堆的底部X显然是比较小的数,才会在底部),然后再次和堆顶最大值交换,再调整下来。
    从上面看出,堆排序做了许多无用功。

    至于快速排序为啥比归并排序快,我说不清楚。

    2#

    算法复杂度一样只是说明随着数据量的增加,算法时间代价增长的趋势相同,并不是执行的时间就一样,这里面有很多常量参数的差别,即使是同样的算法,不同的人写的代码,不同的应用场景下执行时间也可能差别很大。

    快排的最坏时间虽然复杂度高,但是在统计意义上,这种数据出现的概率极小,而堆排序过程里的交换跟快排过程里的交换虽然都是常量时间,但是常量时间差很多。

    3#

    请问你的快快速排序是怎么写的,我写的快速排序,当测试数组大于5000的时候就栈溢出了。
    其他的几个排序都对着,不过他们呢没有用栈。
    这是快速排序的代码,win7 32位,vs2010.

     1 int FindPos(double *p,int low,int high)
     2 {
     3     double val = p[low];
     4     while (low<high)
     5     {
     6         while(low<high&&p[high]>=val)
     7             high--;
     8         p[low]=p[high];
     9         while(low<high&&p[low]<val)
    10             low++;
    11         p[high]=p[low];
    12     }
    13     p[low]=val;
    14     return low;
    15 }
    16 void QuickSort(double *a,int low,int high)
    17 {
    18     if (!a||high<low)
    19         return;
    20 
    21     if (low<high)
    22     {
    23         int pos=FindPos(a,low,high);
    24         QuickSort(a,low,pos-1);
    25         QuickSort(a,pos+1,high);
    26     }
    27 }


    ……

    7#

    谁说的快排好啊?我一般都用堆的,我认为堆好。

    这个哪有谱啊,考察使用环境啊。不是只有时间代价一种衡量标准的,还有空间代价,LZ也有过栈溢出的经历,那不就是空间代价过高了嘛。改成别的办法还是避免不了空间代价。LZ自己不是也已经把空间待见列出来了嘛。

    ……

    11#

    我自己按照堆排序的算法思想实现了堆排序,写完后对照网上的代码,代码几乎是差不多的。
    至于为何堆排序效率比希尔排序慢很多,我在上面帖子已经说了,是堆排序的算法缺陷造成无用功太多。(我把堆排序看成是高级排序中的冒泡排序,冒泡也是两两交换做了太多无用功所以最慢。)
    贴下我实现的希尔排序和堆排序,你可以自己去运行比较。

     1 template <typename T>
     2 void ShellSort(T arr[], int n)          //希尔排序就是增量逐步缩小的直接插入排序
     3 {                                       //这里采用黄金分割的比例逐次减小增量
     4     int gap = n * 0.382 + 0.5;          //所以代码和直接插入排序非常像。区别就是把增量1换成gap
     5     int i, j;
     6     T temp;
     7     while (gap > 0)
     8     {
     9         for (i = gap; i < n; i++)
    10         {
    11             temp = arr[i];
    12             for (j = i - gap; j >= 0; j -= gap)
    13             {
    14                 if (temp < arr[j])
    15                     arr[j + gap] = arr[j];
    16                 else
    17                     break;
    18             }
    19             arr[j + gap] = temp;
    20         }
    21         gap = gap * 0.382 + 0.5;
    22     }
    23 }


     

     1 template <typename T>
     2 void HeapSort(T arr[], int n)
     3 {
     4     for (int i = n / 2 - 1; i >= 0; i--)    //建立最大堆,从最后一个非叶节点开始
     5         MaxHeapify(arr, n, i);              //对于N规模的堆(0,n-1), n/2-1是最后一个非叶节点
     6 
     7     T temp;
     8     for (int i = n - 1; i > 0; i--)   
     9     {
    10         temp = arr[i];               //每次取堆顶数据和堆底部数据交换
    11         arr[i] = arr[0];             //并逐步缩小堆的大小
    12         arr[0] = temp;
    13         MaxHeapify(arr, i, 0);       //重新筛选堆
    14     }
    15 }
    16 
    17 
    18 template <typename T>
    19 void MaxHeapify(T arr[], int n, int i)
    20 {
    21     T temp, max_data;
    22     int pos = i;
    23     int left = pos * 2 + 1;
    24     int right = left + 1;
    25     int max_pos;
    26     temp = arr[pos];
    27 
    28     while (left < n)     //至少有左子树
    29     {
    30         //取左右子树的最大值
    31         max_data = arr[left];
    32         max_pos = left;
    33         if (right < n)   //左右子树均有
    34             if (arr[right] > arr[left])
    35             {
    36                 max_data = arr[right];
    37                 max_pos = right;
    38             }
    39 
    40         if (temp < max_data)         //节点比左右子树最大值小,继续向下调整
    41         {
    42             arr[pos] = max_data;
    43             pos = max_pos;
    44             left = pos * 2 + 1;
    45             right = left + 1;
    46         }
    47         else
    48             break;
    49     }
    50     arr[pos] = temp;   //回填
    51 }

    12#

    Mark一下, 有时间好好做一些实验,给出具体结果。

    这里先说说的我的看法,希望实验能支持我的预测。
    我认为,
    1. 快排的时间复杂度是不稳定的,在最快情况下比归并排序慢的多。
    2. 当数据量大时,充分优化的归并排序可比快速排序更快。其原因有
      1). 归并排序对内存的访问是严格的顺序方式(3个2个源数组,1个目标数组,都是顺序放分),故cache的命中率比快排更高,从这点上,相同的内存读写操作,归并优于快排,当数组占用的空间大大超过cache的大小,则这一优势将更加明显。
      2)普通写法的归并排序有2个缺点,如果改进,则可以提速。如果你的实验是基于最普通的版本,得到的结果是快排优于归并,而优化的归并排序的版本,其性能则可能反超快排。
      2.1) 归并排序不是In place.需要将结果存储到临时缓冲区,然后在复制回来,这个过程可以优化掉。使用乒乓做法,在第i级归并过程,从buff1 归并到buff2,在i+1级归并过程,从buff2复制到buff1。
      2.2) 2路归并排序的核心动作是比较2个对列的头元素那个更大,其比较结果是随机的,2个分支机会均等,CPU的分支预测算法不起作用,当预测失败,可大大降低程序性能,如果消除这个分支,可明显提高程序性能。

    13#

    楼主知道标准库的sort是怎么实现的么? 先快排,递归深度超过一个阀值就改成堆排,然后对最后的几个进行插入排序。    //233系统真机智

    14#

    1.快排的时间复杂度确实不稳定,极端情况是O(n^2),但是平摊下来是T(n*lg(n)),而归并是严格的O(n*log(n))。
    2.快速排序比归并排序快。其原因有
      1)快排对内存的访问是顺序方式(包括逆序),只有两个目标而且是同一个数组,故cache的命中率不会比归并低。特别是数组空间接近于cache大小时,这一优势将更加明显。
      2)快排的内存写操作次数平摊下来是T(n*lg(n)/2),而归并的内存写操作次数是严格的O(n*log(n)),由于内存写操作开销比较大,所以对于随机数据快排优于归并。

    ……

    16#

    同学们,这些算法都有标准的开源程序。
    自己写程序对理解算法的确有好处。
    但是如果用于得到一般性的比较结果,那是靠不住的。

    原帖到此终结;

    文自:http://bbs.csdn.net/topics/390554511

  • 相关阅读:
    Java 中字符串的格式化
    Awk学习笔记
    Spring任务调度实战之Quartz Simple Trigger
    Subclipse vs. Subversive
    使用OpenSSL生成自用证书
    httpclient处理页面跳转
    巧用TableDiff(转)
    DBCC了解页面结构
    取最新一条SQL优化
    SQL 2008 到 SQL2012的镜像
  • 原文地址:https://www.cnblogs.com/Leroscox/p/6160468.html
Copyright © 2020-2023  润新知