STL之std::sort集大家之所长

三种排序算法

在学习c++的过程中,对std::sort函数的实现有了粗浅的认识,了解的越深入越惊叹开发人员代码的美妙之处。原以为的不起眼之处却暗藏玄机,在查阅了各种文章后,觉得有必要记录一下(搬运工),仅以此不断勉励自己。

一、快速排序

首先直面快速排序,名如其名,以快速著称,平均时间复杂度达到O(N logN),是最快速的排序算法之一。它采用了递归的函数调用原理,不断地选择pivot(基准点),将小于它的放在前面,将大于它的放在后面,直到全排序。
然而快排存在两个缺点:
其一:在选择pivot最糟糕(每次选取的pivot是最大值或者最小值,意义不大)的情况下,它的最差时间复杂度恶化到了O(N^2),这就衍生出了诸多优化技巧和策略。

其二:由于快排的递归实现原理(递归所带来的弊端),递归是一种函数调用,它会产生一些额外的开销,比如返回指针、参数压栈、出栈等,在分段很小的情况下,过度的递归会带来过大的额外负荷,从而拉缓排序的速度。
动态过程如下:
在这里插入图片描述

二、堆排序

其次,速度能与之匹敌的堆排序出场了。虽然两者具有一样的时间复杂度O(N logN),但是就平均表现而言,堆排序要比快排慢2~5倍,为啥呢?

具体原因在于:heap sort make poor use of cache memory
粗略解释一下:快排在递归进行部分的排序的时候,只会访问局部的数据,因此缓存能够更大概率的命中;而堆排序的建堆过程是整个数组各个位置都访问到的,后面则是所有未排序数据各个位置都可能访问到的,所以不利于缓存发挥作用。简单的说就是快排的存取模型的局部性(locality)更强,堆排序差一些。

即速度和缓存的问题都反映了堆排序让数据过于大距离的移动,你观察某个元素在整个排序过程中的移动过程,会发现它是前后大幅度的跑动;而快速排序则是尽快的移动到最终的位置,然后做小范围的跳动。

优点:堆排序具有其自己的独特优点,那就是它的最坏情况下的时间复杂度仍然保持O(N logN),这点比快排好。堆排序内部其实也是调用了递归,一般是先构建二叉树结构,再进行结点调整,整个算法框架是固定两个步骤的,所以它的时间复杂度最差也是O(N logN)
动态过程如下:
在这里插入图片描述

三、插入排序

最后提到插入简单的插入排序(稳定的),从末尾开始,将元素有序的往前比较插入。最好的排序情况(已经全排好)时间复杂度为O(N),最差的情况为O(N^2)
优点,在大量元素已经排好的情况下,当然效率更高。
动态过程如下:
在这里插入图片描述

四、STL之std::sort函数

之所以提到前面三种算法,全是为了解sort函数做铺垫。接着便是集大家之所长的一种混合式优化算法横空出世。sort集成了前三种算法各自的优点:

sort粗略原理

  • 数据量很大时采用正常的快速排序,此时效率为O(N logN)。
  • 一旦分段后的数据量小于某个阈值,就改用插入排序,因为此时这个分段是基本有序的,这时效率可达O(N)。
  • 在递归过程中,如果递归层次过深,分割行为有恶化倾向时,它能够自动侦测出来,使用堆排序来处理,在此情况下,使其效率维持在堆排序的O(N logN),但这又比一开始使用堆排序好。

由此可知,它乃综合各家之长的算法。也正因为如此,C++的标准库就用其作为std::sort的标准实现。

深究原理转至大神博客: 链接.

sort 适合那些容器

这么高效的算法,是不是所有的容器都可以使用呢?我们常规数组是否也能使用?我们知道在STL中的容器可以大致分为:

  1. 序列式容器:vector, list, deque
    序列式容器中,vector和deque拥有随机访问迭代器,因此它们可以使用std::sort排序。而list只有双向迭代器,所以它无法使用std::sort,但好在它提供了自己的sort成员函数。
  2. 关联式容器:set, map, multiset, multimap
    对于所有的关联式容器如map和set,由于它们底层是用红黑树实现,因此已经具有了自动排序功能,不需要std::sort。至于配置器容器,因为它们对出口和入口做了限制,比如说先进先出,先进后出,因此它们也禁止使用排序功能。
  3. 配置器容器:queue, stack, priority_queue
  4. 无序关联式容器:unordered_set, unordered_map, unordered_multiset, unordered_multimap。这些是在C++ 11中引入的。
    由于std::sort算法内部需要去取中间位置元素的值,为了能够让访问元素更迅速,因此它只接受有随机访问迭代器的容器。对于所有的无序关联式容器而言,它们只有前向迭代器,因而无法调用std::sort。但我认为更为重要的是,从它们名称来看,本身就是无序的,它们底层是用哈希表来实现。它们的作用像是字典,为的是根据key快速访问对应的元素,所以对其排序是没有意义的。

另外,我们最常使用的数组其实和vector一样,它的指针本质上就是一种迭代器,而且是随机访问迭代器,因此也可以使用std::sort。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值