数据结构与算法-基础算法篇-排序优化(如何实现一个通用的、高性能的排序函数?)

5. 排序优化:

1. 如何选择合适的排序算法?
  1. 排序算法一览表
    img
  2. 为什选择快速排序?
    1. 线性排序时间复杂度很低但使用场景特殊,如果要写一个通用排序函数,不能选择线性排序。
    2. 为了兼顾任意规模数据的排序,一般会首选时间复杂度为O(nlogn)的排序算法来实现排序函数。
    3. 同为O(nlogn)的快排和归并排序相比,归并排序不是原地排序算法,所以最优的选择是快排。

2. 如何优化快速排序?

  1. 导致快排时间复杂度降为O(n)的原因是分区点选择不合理,最理想的分区点是:被分区点分开的两个分区中,数据的数量差不多。如何优化分区点的选择?有2种常用方法,如下:

    1. 三数取中法

      从区间的首、中、尾分别取一个数,然后比较大小,取中间值作为分区点。

      如果要排序的数组比较大,那“三数取中”可能就不够用了,可能要“5数取中”或者“10数取中”。

    2. 随机法:

      每次从要排序的区间中,随机选择一个元素作为分区点。

  2. 警惕快排的递归发生堆栈溢出,有2中解决方法,如下:

    1. 限制递归深度,一旦递归超过了设置的阈值就停止递归。
    2. 在堆上模拟实现一个函数调用栈,手动模拟递归压栈、出栈过程,这样就没有系统栈大小的限制。
  3. 通用排序函数实现技巧

    1. 对于小规模数据的排序,O(n2) 的排序算法并不一定比 O(nlogn) 排序算法执行的时间长。对于小数据量的排序,我们选择比较简单、不需要递归的插入排序算法

    2. 为什么说:O(n2) 时间复杂度的算法并不一定比 O(nlogn) 的算法执行时间长

      时间复杂度代表的是一个增长趋势,如果画成增长曲线图,你会发现 O(n2) 比 O(nlogn) 要陡峭,也就是说增长趋势要更猛一些。但是,我们前面讲过,在大 O 复杂度表示法中,我们会省略低阶、系数和常数,也就是说,O(nlogn) 在没有省略低阶、系数、常数之前可能是 O(knlogn + c),而且 k 和 c 有可能还是一个比较大的数。
      
      假设 k=1000,c=200,当我们对小规模数据(比如 n=100)排序时,n2的值实际上比 knlogn+c 还要小
      
      knlogn+c = 1000 * 100 * log100 + 200 远大于10000
      n^2 = 100*100 = 10000
      
      所以,对于小规模数据的排序,O(n2) 的排序算法并不一定比 O(nlogn) 排序算法执行的时间长。对于小数据量的排序,我们选择比较简单、不需要递归的插入排序算法。
      
    3. 防止堆栈溢出,可以选择在堆上手动模拟调用栈解决

    4. 在排序区间中,当元素个数小于某个常数是,可以考虑使用O(n^2)级别的插入排序

    5. .用哨兵简化代码,每次排序都减少一次判断,尽可能把性能优化到极致

3. 补充

java1.8中的排序,在元素小于47的时候用插入排序,大于47小于286用双轴快排,大于286用timsort归并排序,并在timesort中记录数据的连续的有序段的的位置,若有序段太多,也就是说数据近乎乱序,则用双轴快排,当然快排的递归调用的过程中,若排序的子数组数据数量小,用插入排序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值