快速排序边界条件
快速排序的核心思想是分治,即将一个序列划分成左部分小于等于x,右部分大于等于x
其中设置两个指针i,j
(i从左边,j从右边)从两端开始,
i
遇到大于等于x
的停下来,j
遇到小于等于x
的停下来,然后如果(i<j)
就交换q[i],q[j],之后结束循环,递归
因为左部分小于等于x,右部分大于等于x
,所以递归只有两种情况
即sort(l,i-1),sort(i,r);
或者 sort(l,j),sort(j+1,r);
对于i
来说,i
的左边的值是确定的,一定小于等于x
,
对于j
来说,j
的右边的值是确定的,一定大于等于x
,
(最后结束时i >= j
的右边(i在j的右边或者相等)
此时可以得出结论:
i
左边的值都是小于等于x
,但如果i > j
,q[i]
不能保证小于等于x
,但q[i-1]
可以保证小于等于x,q[i]
大于等于x
. 所以此时保证区间[l, i - 1] 小于等于x, [i , r] 大于等于x,进行递归sort(l, i - 1),sort(i, r);
如果用j
也是同样道理, q[j]不能保证大于等于x
,但可以保证小于等于x
(因为i>=j)此时sort(l,j),sort(j + 1,r);
当x=q[l]
的边界情况
当只有两个元素时例如4,5
,这时按照代码执行下来,循环之后i=l
,退出循环后sort(l,i-1),sort(i,r);
等价于sort(l,i-1),sort(l,r);
这就跟进入递归时一样了
,就导致了死循
环,所以此时只能用sort(l,j),sort(j+1,r);
当x = q[r]
的边界情况
道理类似,如果此时为4,5
也会导致j=r
,进行sort(l,j),sort(j+1,r);
导致死循环(一直sort(l,r)
,此时只能用sort(l,i-1),sort(i,r);
当x=q[l+r>>1]
的边界情况
此时x取的是序列中间靠左的位置(如果序列个数为奇,则取正中间,如果为偶,则取中间靠左),此时如果元素个数为2,
则中间靠左就是第1个元素,这时就跟x=q[l]的边界情况一致了,所以这时只能用sort(l,j),sort(j+1,r);
当x=q[l+r + 1>>1]
的边界情况
此时x取的是序列中间靠右的情况,同理,当元素只有两个,情况就会类似x=q[r]
,此时只能用sort(l,i-1),sort(i,r);
总结,
- 如果
x=q[l] or x=q[l+r>>1]
此时只能用sort(l,j),sort(j+1,r)
; - 如果
x=q[r] or x=q[l+r + 1>>1]
此时只能用sort(l,i-1),sort(i,r)
;
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
int q[N],n;
void quick_sort(int l, int r)
{
if(l >= r)return;
int x = q[l + r >> 1];
int 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(l,j);quick_sort(j + 1,r);
}
int main()
{
cin >> n;
for(int i = 0; i < n; i ++)cin >> q[i];
quick_sort(0,n-1);
for(int i = 0; i < n; i ++)cout << q[i] << ' ';
return 0;
}
还有一点需要注意:
1. 循环里面是while(i < j)
不是 while(l < r)
2. 内部循环是while(q[i] < x) 只有小于号, 没有等于号。 如果有等于,逆序的数组会出界
最好的题解
一下子就解决了我之前边界不太理解的问题,膜拜大佬
orz
非常好解析
妙
自己想到了这点hhh
你好,这里当
** x=q[r] **
时,举的例子应该还是 4,5 吧,要是5,4的话不会导致**j=r**
对的对的,感谢老哥指出。已修改
很快啊,一下子就爱上这个解释
不过我还想问问大佬,最后内部循环中的while(q[i] < x),如果有等于的话逆序数组会出界是什么意思啊,我的对不能有等号的理解是如果有等号的话,x的位置不是从始至终就没有改变过,也就是没有正常的排序
啊啊啊,我的错,我的意思是,如果有等于号,比如说这个数组是3,2,1,4,5,如果你刚好取了中间最小的1,你的
do j --;while(q[j] >= 1);
会一直向左,就会出界。大佬!
老哥写的太好了!
明白