快排
思路想法:
取一个分界值,然后将当前序列按照这个值,分成两个部分,一个部分是小于这个值的,一部分是大于这个值的
这个时候已经将一个乱序的序列分成了两个部分,然后同时再对这两个部分同样进行相同的排序操作
依次递归下去,直到只剩一个数的时候就不用遍历了,这时候所有的数都已经排列好了
实现步骤:
-
设置递归停止条件:
if(l >= r)return;
这个是停止的条件,就是当需要排序的时候这时只有一个数,所以并不需要排序,直接结束
-
定义一个分界线mid(值):
int mid = a[l + r >> 1];
或int mid = a[ l + r +1 >> 1];
这边取的是一个值,通过对比大小来进行区分左右两边
原则上随便取一点都可以,一般取第一个数,中间那个数,或者最后一个数
但是第一个数跟最后一个数经常会发生边界问题,所以一般取中间那个数 -
实现递归排序:将当前序列分成两个部分
采用双指针法 首先定义两个指针,一个指向头,一个指向尾
int i = l - 1, j = r + 1;
-
核心部分:循环实现排序
如果头指针所指向的数,小于mid分界值,那么就往右走,直到遇到大于或等于他的数则停止
同样,如果尾指针所指向的数,大于mid分界值,那么就往左一直走,直到遇到小于或等于他的数则停止
这时候两个指针所指的数都是不属于他们分区的数,如果第一个指针小于第二个指针(说明:第一个指针指向的是第一个分区,第二个指针指向的是第二个分区)则将他们两个进行交换
-
最后对两个区间再进行递归排序
同时对大于mid 的区间以及小于mid 的区间进行递归分区
注意部分:
-
初始化双指针的时候多减1
是因为下面排序的时候,使用的do…while()…
所以再进行判断的时候会先执行一次操作,所以初始化的时候要先-1,或+ 1;
-
递归排序的时候为什么选用j不选用i,还有为什么第二个部分要j+1;
这个跟之前所选用的mid 是有关系的:
首先,整数除2是会向下取整的,所以
mid = [l + r >> 1]
是想下取整的,所以当只有两个数的时候他是取第一个数作为标准的**特别说明:**排序完成后两个指针的位置问题,结束后头指针和尾指针会停留在同一个位置,或者形成交叉,所有取头指针的时候要-1.取尾指针的时候要+1;那么取哪个指针跟mid是向上或者向下取整有关系
这个时候有两种选择:选择i,和选择j
这个时候如果选择i的话会进去无限递归中,所以要选择j
代码
#include<iostream>
using namespace std;
const int N = 100010;
int a[N];
void quick_sort(int a[],int l,int r)
{
if(l >= r)return ;
int mid = a[l + r >> 1];
int i = l - 1, j = r + 1;
while(i < j)
{
do i++; while(a[i] < mid);
do j--; while(a[j] > mid);
if(i < j)swap(a[i], a[j]);
}
quick_sort(a, l ,j);
quick_sort(a,j+1,r);
}
int main()
{
int n;
cin>>n;
for(int i = 0; i < n; i++)scanf("%d",&a[i]);
quick_sort(a,0,n-1);
for(int i = 0; i < n; i++)printf("%d ",a[i]);
return 0;
}
quick_sort(a,0,n-1);
for(int i = 0; i < n; i++)printf("%d ",a[i]);
return 0;
}