快速排序
一、核心思想
递归;二分(分治)
二、算法实现
1、 选标杆 x 定两边 左l右r
2、 设计算法来实现 <=x的在左边,>=x的在右边(前提 升序)
可采用“双指针”:
① 左边 i 右边 r (前提是 i<j 作为合法条件)
② 左右两边都开始寻找直到不满足划分条件时,“停下”
③ 交换i r (i<j)
④ 一旦i<j不合法 则结束循环
3、 递归
三、代码实现
//首先,指明出现无限划分的情况————
//划分后的数据的数量:左0右n,或者左n右0
//(主要是因为数量为n的区间,如果数量为0则会被递归结束条件直接return)
void quick_sort(int q[], int l, int r)
{
//递归一定要设置 递归结束条件
//l>=r很显然,此时所划分的区间只有一个数或者没有数
if(l >= r) return;
//实现区间内的排序
//i=l-1 对应着下面循环语句中的i++,便于语句的书写,同时也能进入下方循环
/*
x的取值与划分区间的边界有关,
一般选取中间位置的值(>>1右移1就是/2),或者随机取值(前提是不影响边界)
*/
int i = l - 1, j = r + 1, x = q[l + r >> 1];
//此处i<j,不可以取= ,
//当i==j时,有可能因为数组越界而导致奇奇怪怪的问题(可以模拟{0,1})
while(i < j)
{
//左右两边都开始寻找直到不满足划分条件时,“停下”
/*
while(q[i] < x)中 不能取=x 的情况
当数据全部相等时可能会出现无限划分的情况
*/
do i++; while(q[i] < x);
do j--; while(q[j] > x);
//此处if(i < j),可以取=
if(i < j) swap(q[i], q[j]);
}
//递归再细化
/*
注意边界问题可能造成的无限划分:
① 最容易写错的一个错误写法就是左边l到r,右边j到r————
模拟{0,1}可知:第一次划分区间为{0},{0,1}(q[i]=0,q[j]=0)
于是右侧区间数量成为n,开始无限划分
所以为了使划分的区间是不重叠的,因此要选择一个指针和该指针的下一个作为边界
② 此时会引出另一个问题,比如选择j,那x的位置不能选择r——
第一次划分也许{...<=q[r]}{q[r]>=...}
第二次划分对于右侧来说x会变成最右边的数据
但是左侧依旧是q[r],左侧始终满足<=q[r],则后续划分还将是左侧数据开始无限划分
因此一般 x取中间位置的数据(注意是中间位置,而不是中间值)
边界选取j与j+1
*/
quick_sort(q, l, j), quick_sort(q, j + 1, r);
}
int main()
{
int a[50]={0,1},n=2;
//数组a,下标0,n-1
quick_sort(a,0,n-1);
for(ll i=0;i<n;i++){
cout<<a[i]<<" ";
}
}
四、注意
但实际上由于 C++ algorithm 库中已经自带 std::sort 函数,你并不需要手写快排。
std::sort在快排的基础上加入了一些优化,比如在分治达到一定深度时改用堆排序,从而使得最差时间复杂度为 O(nlogn*) 。因此,你手写快排也只会被 std::sort 慢。