2019-12-07
快速排序
相关
D0001 D0002 D0003
首先是原理
快速排序是基于分治思想的一种算法。
大致做法是
- 确定分界点x,即在所有待排序中的数中选择一个数字为分界点。例如待排序的数组为a[l]-a[r],l和r是数组的左右边界,x可以取a[l],a[r],a[(l+r)/2],a[随机](随机数在l-r之间)。
- 调整区间,所有小于等于x的在左边,所有大于等于x的数在右边。
- 递归处理左右两段,直至无法处理。
需要注意的是边界问题,如何处理边界的快捷方式是背一个测试没有问题的模板,可以有效减少在比赛时的调试时间。
第二种调整区间的做法有很多,但是要尽量做到不开临时空间,且时间复杂度合理。
较好的办法:可以在数组的两端各放一个指针,向中间运行,当左边的指针大于选定x且右边指针小于x时,交换左右数值。
当两指针相遇的时候即完成第一次处理,接下来递归处理左右两段,反复递归直到最后。
纯递归快排:
代码:
#include<iostream>
using namespace std;
int n;
int q[100010];
void quick_sort(int q[], int l, int r)
{
if (l >= r) return;
int x =q[l], i = l - 1, j = r + 1;
while (i < j)
{
do i++; while (q[i] < x);
do j--; while (q[j] > x);
if (i < j) swap(q[i], q[j]);
}
quick_sort(q, l, j);
quick_sort(q, j + 1, r);
}
int main()
{
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> q[i];
}
quick_sort(q, 0, n - 1);
for (int i = 0; i < n; i++)
{
cout << q[i] << " ";
}
return 0;
}
这样的快速排序有的时候会被卡,主要是因为如果x取数组左端的话,数据为顺序的时候该代码会劣化为 O(n2) (?大概是这么算的吧,有错误的话请指正),相当于冒泡排序,时间复杂度会大大增加。可以考虑使用随机取值以及左中右三者取中间值的办法。
稍加一点点优化的快排:
代码:
#include<iostream>
#include<ctime>
using namespace std;
int n;
int q[100010];
void quick_sort(int q[], int l, int r)
{
if (l >= r) return;
if (r - l < 10) //这里是为了减少递归次数,最后的几次相对于递归,插入排序的消耗会更少(但是测出来没多少差距,可能是数据量依然不够)。
{
int i, j;
int temp;
for (i = l + 1; i < r + 1; i++)
{
temp = q[i];
for (j = i; j > l&& q[(j - 1)] > temp; j--)
{
q[j] = q[j - 1];
}
q[j] = temp;
}
return;
}
srand((unsigned)time(NULL));
int pivotPos = rand() % (r - l) + l;
int x = q[pivotPos], i = l - 1, j = r + 1;//使用随机数取x避免时间复杂度劣化。
// int x = q[l]>=q[(l+r)/2]?(q[(l+r)/2]>=q[r-1]?q[(l+r)/2]:(q[l]>=q[r-1]?q[r-1]:q[l])):(q[l]>=q[r-1]?q[l]:(q[(l+r)/2]>=q[r-1]?q[r-1]:q[(l+r)/2])), i = l - 1, j = r + 1;
//注释的这一段是使用了问号表达式取q[l],q[(l+r)/2],q[r-1]中的中位数,同样是可以一定程度上的避免时间复杂度劣化。
//不要学习这种问号表达式的方法,只是突发奇想,使用if判断,会使代码更加简单明了。(避免被队友打死)
while (i < j)
{
do i++; while (q[i] < x);
do j--; while (q[j] > x);
if (i < j) swap(q[i], q[j]);
}
quick_sort(q, l, j);
quick_sort(q, j + 1, r);
}
int main()
{
std::ios::sync_with_stdio(false);//关闭输入流同步提高读入速度(貌似c++17上提升的不多,但是对于较老版本提升很大。同样,如果仍然超时可以尝试使用快读。
cin >> n; //排序n个数
for (int i = 0; i < n; i++)
{
cin >> q[i];
}
quick_sort(q, 0, n - 1);
for (int i = 0; i < n; i++)
{
cout << q[i] << " ";
}
return 0;
}
上面的模板中加入了少量的优化,可以在超时卡在刚好差一点点的时候使用。更加深入的优化等之后更新。
没了