快速排序
我居然现在还不会快排。
第一种写法:
// luogu-judger-enable-o2
#include <cstdio>
using namespace std;
const int maxn=1e5+5;
int n, a[maxn];
void swap(int &a, int &b){
int tmp=a; a=b; b=tmp; }
void sort(int l, int r){ // 代表[l,r)
//两哨兵太难写了,一哨兵大X好
if (l+1>=r) return;
int std=a[l], now=l;
for (int i=l+1; i<r; ++i) //比std小的都排到左端
if (a[i]<std) swap(a[i], a[++now]); //能取等号?
swap(a[l], a[now]);
sort(l, now); sort(now+1, r); //now+1,避免死循环
}
int main(){
scanf("%d", &n);
for (int i=1; i<=n; ++i) scanf("%d", &a[i]);
sort(1, n+1);
for (int i=1; i<=n; ++i) printf("%d ", a[i]);
return 0;
}
这种单哨兵的方法,和双哨兵且有一个哨兵带等号的方法一样,都有一个致命缺陷——当序列中数字全部相同时,时间退化到n^2。这就是交这个程序到洛谷上tle的原因。于是我找到了写法二——
#include <cstdio>
using namespace std;
const int maxn=1e5+5;
int n, a[maxn];
void swap(int &a, int &b){
int tmp=a; a=b; b=tmp; }
void sort(int l, int r){ //左闭右开区间
if (l+1>=r) return;
int std=a[(l+r)>>1];
int i=l, j=r-1;
while (i<=j){ //避免撞车使i和j没有到预定位置,加上等号
//注意两个while不能带等号,不然i或j可能一冲到底
//,造成区间大小不变,陷入死循环
while (a[i]<std) ++i;
while (a[j]>std) --j;
//可能i和j都冲过头了,要避免这种情况下交换
if (i<=j){ //同理避免撞车,加上等号
swap(a[i], a[j]);
//若来到了与基准值相等的区域,就需要用蛮力推进了
++i; --j;
}
}
sort(l, j+1); sort(i, r);
}
int main(){
scanf("%d", &n);
for (int i=1; i<=n; ++i) scanf("%d", &a[i]);
sort(1, n+1);
for (int i=1; i<=n; ++i) printf("%d ", a[i]);
return 0;
}
大部分注意点但都写在代码中了,我来解释一下。首先,用左闭右开区间,是因为看了刘汝佳的书,书中说这种区间表示法更好。while中不能带等号,不然要么死循环,要么超时。由于区间的划分时按照i和j来决定的,所以i必须正好在基准值右侧,j必须正好在基准值左侧,才能保证在长度为二的区间中不出现循环情况。因此,i<j的判定要加上等号,这样j一定小于i。
然而,后来我又在寻思如何用第二种写法求第k小数。我原本以为第二种写法会漏过与基准相同的数,但这其实一点关系都没有,不会影响结果(仔细想一想)。不过第二种写法还是比较难写的。
还有,第一种写法是可以改进的。只要找出等于基准值的区间,然后取中值即可。虽然较慢(常数在第二种写法的1.5倍左右),但是个人很喜欢这种写法。
#include <cstdio>
using namespace std;
const int maxn=1e5+5;
int n, a[maxn];
void swap(int &a, int &b){
int tmp=a; a=b; b=tmp; }
void sort(int l, int r){ // 代表[l,r)
//两哨兵太难写了,一哨兵大X好
if (l+1>=r) return;
int std=a[(l+r)>>1], now=l;
for (int i=l; i<r; ++i) //比std小的都排到左端
if (a[i]<std) swap(a[i], a[now++]);
int tmp=now; //[l,tmp)是比std小的
for (int i=tmp; i<r; ++i) //和std相等的都排到右端
if (a[i]==std) swap(a[i], a[now++]);
//[tmp,now)是和std相等的
sort(l, tmp); sort(now, r);
}
int main(){
scanf("%d", &n);
for (int i=1; i<=n; ++i) scanf("%d", &a[i]);
sort(1, n+1);
for (int i=1; i<=n; ++i) printf("%d ", a[i]);
return 0;
}