边界问题很麻烦
参考博客
#include
using namespace std;
const int N = 100010;
int q[N];
void quick_sort(int l, int r){
//只有一个元素 或 不存在元素
if (l >= r) return;
//i在l前一位开始,j在r右一位开始
int i = l - 1, j = r + 1;
//找到中间位的元素mid做一个大小判别标准
//如果用i做分界线的话要向上取整mid = q[l + r + 1 >> 1];
//如果用j做分界线的话要向下取整mid = q[l + r >> 1];
int mid = q[l + r >> 1];
//注意循环条件是i
while (i < j) {
//直到i找到左边第一个不小于mid的元素
do {
i ++ ;
} while (q[i] < mid);
//直到j找到右边第一个不大于mid的元素
do {
j -- ;
} while (q[j] > mid);
//如果两个元素同时存在就进行交换
if (i < j)
swap(q[i], q[j]);
}
//对左右两边进行同样的二分操作
// 以下是用i为界限的写法
// quick_sort(l, i + 1);
// quick_sort(i, r);
quick_sort(l, j);
quick_sort(j + 1, r);
}
int main(){
int n;
cin >> n;
for(int i = 0; i < n; i ++)
scanf("%d", &q[i]);
quick_sort(0, n - 1);
for(int i = 0; i < n; i ++)
printf("%d ", q[i]);
return 0;
}
/*
问:为什么是[l,i-1][i,r]?
答:因为i会先自加,最后位置会靠后
问:为什么是[l,j][j+1,r]?
答:因为j会先自减,最后位置会靠后
问:关于边界问题。
答:
因为这是一个二分的问题,所以边界问题至关重要
我们担心n个数出现划分成0和n两部分无限划分的情况
对于取i还是j,和边界有关
结论如下:
以j为划分时,x不能选q[r],向下取整
若以i为划分,则x不能选q[l],向上取整
举例1:
以j划分时,我们盯着j看
可证:j的最小值是l
证明: j的最小值是l
最极端的情况是只有两个数的时候
只有此时j能取到l
因为是先do,所以第一轮i肯定会来到l
而j无论如何通畅地往前移动都会在mid这里停一下
交换之后i在第二轮会自加来到第l+1位
证明完毕。
所以q[j+1...r]不会造成无限划分
那么q[l...j]会出问题,因为j可能取到r
如 2,3
最后i和j都在3这个位置
l~j这个子区间与l~r重合了
无限划分出现了!
举例2:
以i划分时,i的最大值是r(和j最小是l同理)
那么q[i,r]会出问题,因为i可能取到l(mid向下取整时)
如2,3,mid向下取整就是2
最后i,j会停在2的位置
此时[i,r]与[l,r]重合([l,i - 1]无事)
无限划分出现了!
数组中q[l,r-1] < x,
那么这一轮循环结束时i = r, j = r
下次划分就是(l,j)(j+1,r),前者即(l,r),无限循环了
*/