经典四讲贯通C 排序之四 选择排序
创始人
2024-07-25 15:11:09
0

  我们都知道C++排序方法中,有四种常用方法插入排序希尔排序交换排序以及选择排序。这篇文章我们介绍选择排序。(本系列文章统一 测试程序

  选择排序

  基本思想是:每次选出第i小的记录,放在第i个位置(i的起点是0,按此说法,第0小的记录实际上就是最小的,有点别扭,不管这么多了)。当i=N-1时就排完了。

  直接选择排序

  直选排序简单的再现了选择排序的基本思想,***次寻找最小元素的代价是O(n),如果不做某种特殊处理,每次都使用最简单的寻找方法,自然的整个排序的时间复杂度就是O(n2)了。

  1. template   
  2. void SelectSort(T a[], int N, int& KCN, int& RMN)  
  3. {  
  4. KCN = 0; RMN = 0;  
  5. for (int i = 0; i < N; i++)  
  6. {  
  7. for (int j = i + 1, k = i; j < N; j++) if (++KCN && a[j] < a[k]) k = j;//select min  
  8. if (k != i) { swap(a[k], a[i]); RMN += 3; }  
  9. }  

  测试结果:

  1. Sort ascending N=10000 TimeSpared: 721ms  
  2. KCN=49995000 KCN/N=4999.5 KCN/N^2=0.49995 KCN/NlogN=376.25  
  3. RMN=0 RMN/N=0 RMN/N^2=0 RMN/NlogN=0  
  4. Sort randomness N=10000 TimeSpared: 711ms  
  5. KCN=49995000 KCN/N=4999.5 KCN/N^2=0.49995 KCN/NlogN=376.25  
  6. RMN=29955 RMN/N=2.9955 RMN/N^2=0.00029955 RMN/NlogN=0.225434  
  7. Sort descending N=10000 TimeSpared: 711ms  
  8. KCN=49995000 KCN/N=4999.5 KCN/N^2=0.49995 KCN/NlogN=376.25  
  9. RMN=15000 RMN/N=1.5 RMN/N^2=0.00015 RMN/NlogN=0.112886 

  可以看到KCN固定为n(n-1)/2。另外一件有趣的事是,RMN=0的正序花的时间居然比RMN接近3(n-1)的乱序还多。一是说明测试精度不够,在我的机器上多次测试结果上下浮动10ms是常有的事;二是说明和KCN的n(n-1)/2相比,RMN的3(n-1)有些微不足道。

#p#

  锦标排序

  从直选排序看来,算法的瓶颈在于KCN,而实际上,对于后续的寻找最小值来说,时间复杂度可以降到O(logn)。最为直接的做法是采用锦标赛的办法,将冠军拿走之后,只要把冠军打过的比赛重赛一遍,那么剩下的人中的“冠军”就产生了,而重赛的次数就是竞赛树的深度。实际写的时候,弄不好就会写得很“蠢”,不只多余占用了大量内存,还会导致无用的判断。我没见过让人满意的例程(殷版上的实在太恶心了),自己又写不出来漂亮的,也就不献丑了(其实这是惰性的缘故,有了快排之后,大多数人都不会对其他内排感兴趣,除了基数排序)。实在无聊的时候,不妨写(或者改进)锦标排序来打发时间,^_^。

  堆排序

  锦标排序的附加储存太多了,而高效的寻找***值或最小值(O(logn)),我们还有一种方法是堆。这里使用了***堆,用待排记录的空间充当堆空间,将堆顶的记录(目前***)和堆的***一个记录交换,当堆逐渐缩小成1的时候,记录就排序完成了。显而易见的,时间复杂度为O(nlogn),并且没有很糟的情况。

  1. template   
  2. void FilterDown(T a[], int i, int N, int& KCN, int& RMN)  
  3. {  
  4. int child = 2 * i + 1; T temp = a[i];  
  5. while (child < N)  
  6. {  
  7. if (child < N - 1 && a[child] < a[child+1]) child++;  
  8. if (++KCN && temp >= a[child]) break;//不需调整,结束调整  
  9. a[i] = a[child]; RMN++;  
  10. i = child; child = 2 * i + 1;  
  11. }  
  12. a[i] = temp; RMN++;  
  13. }  
  14. template   
  15. void HeapSort(T a[], int N, int& KCN, int& RMN)  
  16. {  
  17. int i;  
  18. for (i = (N - 2)/2; i >= 0; i--) FilterDown(a, i, N, KCN, RMN);//生成***堆  
  19. for (i = N - 1; i > 0; i--)  
  20. {  
  21. swap(a[0], a[i]); RMN += 3;  
  22. FilterDown(a, 0, i, KCN, RMN);  
  23. }  

  测试结果,直接测试的是N=100000:

  1. Sort ascending N=100000 TimeSpared: 110ms  
  2. KCN=1556441 KCN/N=15.5644 KCN/N^2=0.000155644KCN/NlogN=0.937071  
  3. RMN=2000851 RMN/N=20.0085 RMN/N^2=0.000200085RMN/NlogN=1.20463  
  4. Sort randomness N=100000 TimeSpared: 160ms  
  5. KCN=3047006 KCN/N=30.4701 KCN/N^2=0.000304701KCN/NlogN=1.83448  
  6. RMN=3898565 RMN/N=38.9857 RMN/N^2=0.000389857RMN/NlogN=2.34717  
  7. Sort descending N=100000 TimeSpared: 90ms  
  8. KCN=4510383 KCN/N=45.1038 KCN/N^2=0.000451038KCN/NlogN=2.71552  
  9. RMN=5745996 RMN/N=57.46 RMN/N^2=0.0005746 RMN/NlogN=3.45943 

  整体性能非常不错,附加储存1,还没有很糟的情况,如果实在不放心快排的最坏情况,堆排确实是个好选择。这里仍然出现了KCN、RMN多的反而消耗时间少的例子——误差70ms是不可能的,看来CPU优化的作用还是非常明显的(可能还和Cache有关)。

【编辑推荐】

  1. 几种常用的C#排序方法简介
  2. 四种C#排序算法代码示例
  3. C#算法之选择排序浅析
  4. c++编程常用工具
  5. 给C++初学者的50个忠告
  6. c++最基础的20条规则
  7. 深入剖析C/C++程序员应聘常见面试题
  8. 程序员必看 c++笔试题汇总

相关内容

热门资讯

如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
施耐德电气数据中心整体解决方案... 近日,全球能效管理专家施耐德电气正式启动大型体验活动“能效中国行——2012卡车巡展”,作为该活动的...
20个非常棒的扁平设计免费资源 Apple设备的平面图标PSD免费平板UI 平板UI套件24平图标Freen平板UI套件PSD径向平...
Windows恶意软件20年“... 在Windows的早期年代,病毒游走于系统之间,偶尔删除文件(但被删除的文件几乎都是可恢复的),并弹...
德国电信门户网站可实时显示全球... 德国电信周三推出一个门户网站,直观地实时提供其安装在全球各地的传感器网络检测到的网络攻击状况。该网站...
为啥国人偏爱 Mybatis,... 关于 SQL 和 ORM 的争论,永远都不会终止,我也一直在思考这个问题。昨天又跟群里的小伙伴进行...
《非诚勿扰》红人闫凤娇被曝厕所... 【51CTO.com 综合消息360安全专家提醒说,“闫凤娇”、“非诚勿扰”已经被黑客盯上成为了“木...