对于快速排序算法,我们可以用以下自然语言来描述其过程:
1.在待排序数组中选择一个哨兵,将其放在正确位置,使得他左边的数都比他小,右边的数都比他大。
2.让哨兵左侧的数组和哨兵右侧的数组也执行同样过程。
3.递归上述过程直至结束。
考虑一下递归的出口,当数组长度小于等于1时,我们认为该数组有序,因此可写出以下代码。
template<typename T>
auto quick_sort(vector<T>& v) {
if (v.size() < 2) return;//如果长度小于等于1,递归结束
vector<T> lhs, rhs;
const T& pivot = v[0];//取数组第一个元素作为哨兵
copy_if(begin(v) + 1, end(v), back_inserter(lhs), [&](const T& val) { return val <= v[0]; });//将小于等于哨兵的元素塞进lhs
copy_if(begin(v) + 1, end(v), back_inserter(rhs), [&](const T& val) { return val > v[0]; });//将大于哨兵的元素塞进rhs
quick_sort(lhs);//递归左侧数组
quick_sort(rhs);//递归右侧数组
v[lhs.size()] = pivot;
copy(begin(lhs), end(lhs), begin(v));
copy(begin(rhs), end(rhs), begin(v) + lhs.size() + 1);//通过copy将排序完结果赋给原数组v,即v = [lhs] ++ [x] ++ [rhs]
}
嗯,去头去尾只要10行,代码还算简洁,思路清晰,唯一的缺点就是递归过程中不断的copy导致性能比较垃圾,来写一个测试程序简单测测性能。
vector<int> v1;
v1.resize(1e7);
static default_random_engine e(time(nullptr));
uniform_int_distribution<int> rand;
generate(v1.begin(), v1.end(), [&] {return rand(e); });
auto tic = clock();
quick_sort(v1);
cout << clock() - tic << "ms" << endl;
测试环境是MSVC、Release、std=C++17,测试结果大致如下
stl sort | my sort | |
---|---|---|
size=1e5 | 8ms | 57ms |
size=1e6 | 90ms | 606ms |
size=1e7 | 1010ms | 6283ms |
在复杂度的常数阶大约差了6倍,还在可接受范围内。
最后附上所有代码
#include<cassert>
#include<vector>
#include<iostream>
#include<algorithm>
#include<random>
using namespace std;
template<typename T>
auto quick_sort(vector<T>& v) {
if (v.size() < 2) return;
vector<T> lhs, rhs;
const T& pivot = v[0];
copy_if(begin(v) + 1, end(v), back_inserter(lhs), [&](const T& val) { return val <= v[0]; });
copy_if(begin(v) + 1, end(v), back_inserter(rhs), [&](const T& val) { return val > v[0]; });
quick_sort(lhs);
quick_sort(rhs);
v[lhs.size()] = pivot;
copy(begin(lhs), end(lhs), begin(v));
copy(begin(rhs), end(rhs), begin(v) + lhs.size() + 1);
}
int main() {
vector<int> v1;
v1.resize(1e7);
static default_random_engine e(time(nullptr));
uniform_int_distribution<int> rand;
generate(v1.begin(), v1.end(), [&] {return rand(e); });
auto tic = clock();
quick_sort(v1);
cout << clock() - tic << "ms" << endl;
}