c++之stl_sort
文章目录
1.前言
stl_sort,不搜不知道一搜吓一跳,它有一个专有名字,introspective sort
。以快排为核心,配合插排、堆排,将时间复杂度维持在 O ( n l o g n ) O(nlogn) O(nlogn)。
本文,基于C#的Array.Sort来实现,原理是类似的,在实现方面可能会有一些差别。
插排、堆排、快排的实现原理,请戳这里。
2.实现
有了上述三种排序算法的基础,就容易许多了。
元素个数检查(threshold)
。当序列剩余元素小于此值时,使用插排
。在排序有序序列的时候,插排时间复杂度为 O ( n ) O(n) O(n)。当数据量小的时候,插排很快;当数据量大,且无序的时候,指数爆炸 O ( n 2 ) O(n^2) O(n2),效率极低(几个小时无法完成10000w随机数组?,闲得蛋疼)。- 设置
递归深度(depthLimit)
。大家都知道,快排很快,但是时间复杂度不稳定,最快的情况 O ( n l o g n ) O(nlogn) O(nlogn),最慢的情况 O ( n 2 ) O(n^2) O(n2),这是由于递归恶化。选取一个好的pivot
,对效率影响很大(pivot的选取问题已经在之前的博客里优化了)。但无论怎样设计,都不可能永远保证pivot的值接近序列的中值
,所以机智的大佬们设计了一个阈值,当当递归深度大于这个阈值的时候,采用堆排
。堆排虽然时间复杂度稳定在 O ( n l o g n ) O(nlogn) O(nlogn),但是多数情况下,它的效率并没有快排高,堆排的判断分支过多。也就是说,使用堆排是一种“补救措施”
。 - threshold的取值并没有什么数学公式可言,来自于经验;depthLimit的取值则有讲究,具体看源码吧。
3.完整代码
环境:mingw64
如何使用:
#include <iostream>
#include "m_sort.h"
int main(){
int *a = new int[50];
srand(1);
for (size_t i = 0; i < 50; i++)
{
a[i] = rand();
cout << a[i] << " ";
}
// InsertionSort(a, 0, 49, [](const int &a, const int &b) { return a - b; });
// QSort(a, 0, 49, [](const int &a, const int &b) { return a - b; });
<