目录
原理
快排的原理就是找一个数a,把要排序的数组分为三个部分,小于a的一部分数、等于a的一部分数、大于a的一部分数。我们在下文中用小于区、等于区、大于区来指代。
分好部分之后,不管其他数怎么样,a的位置一定跟最后排好序后的a的位置一样,因为最后排好序的数组也是可以分为小于a的部分、等于a的部分、大于a的部分,只不过小于区和大于区内部是有序的而已,等于区完全一致。
这就是快排的核心思想。我们为了降低时间复杂度,可以借鉴归并排序的思路,用递归的方法。每一次快排都将数组分为小于区、大于区、等于区,那么我们下一次快排就直接分别排序小于区和大于区即可。每次都把数组分成三份,排序其中两份,平均时间复杂度是O(NlogN)。
例子
假设有数组2 3 1 4 5 2,要用快排排序。先选择最后一个数:
然后从第一个数开始往后,跟最后一个数比较:
2等于2,不理它,继续往后:
3>2,将3和最后一个数前面的数交换,箭头仍停留在该位置:
这时候,先不管最后那个2,将3作为大于区的成员:
再看当前箭头指向的5,仍然大于2,将5与大于区前面那个数交换,并把大于区的范围往前扩张:
4同理:
目前的大于区和等于区:
这时候1小于2了,把1和等于区的第一个数交换,作为新的小于区成员,并把箭头往前移动:
这时候箭头指向了大于区当中,说明都已经排好序了,将最后的那个2放到等于区里,结束这一轮快排:
之后,我们再对左边的小于区和右边的大于区做快排,1可以不用排,以下是大于区的排序过程:
之后再不断递归,最终整个数组都为有序数组:
代码
快排部分
int *partition(int *arr,int L,int R){//数组、左边界下标、右边界下标
int little_border=L-1;//小于区右边界
int big_boder=R;//大于区左边界
while(L<big_boder){//从头遍历到尾
if(arr[L]<arr[R]){//当前数比选定数小
swap(arr,++little_border,L++);//放在小于区里面,小于区范围++,进入下一个数
}
else if(arr[L]>arr[R]){//当前数比选定数大
swap(arr,--big_boder,L);//放在大于区里面,大于区范围++,停在当前位置
}
else{
L++;//放着不动,相当于等于区,进入下一个数
}
}
swap(arr,big_boder,R);//最后把选定的数放在等于区
int *res=(int *)malloc(sizeof(int)*2);//最后返回一个数组
res[0]=little_border+1;//数组成员有等于区的左边界
res[1]=big_boder;//还有等于区的右边界
return res;//函数返回这个数组的指针,方便我们进行下一次的递归快排
}
其中的swap函数就是最常用的交换函数,自己定义一个就行:
void swap(int *arr,int a,int b){
int tmp=arr[a];
arr[a]=arr[b];
arr[b]=tmp;
}
递归部分
void quick_sort(int *arr,int L,int R){//主函数中调用的函数,参数仍为数组、左边界下标、右边界下标
if(L<R){
swap(arr,L+rand()%(R-L+1),R);/*选择一个随机数作为最后一个数
这也是为什么平均时间复杂度能达到 O(logN)的原因 */
int *p=partition(arr,L,R);//之前函数的返回值,也就是我们分割数组三部分的边界线
quick_sort(arr,L,p[0]-1);//对小于区进行快排
quick_sort(arr,p[0+1],R);//对大于区进行快排
}
}
测试代码
#include"stdio.h"
#include"stdlib.h"
int *partition(int *arr,int L,int R);
void swap(int *arr,int a,int b){
int tmp=arr[a];
arr[a]=arr[b];
arr[b]=tmp;
}
void quick_sort(int *arr,int L,int R){//主函数中调用的函数,参数仍为数组、左边界下标、右边界下标
if(L<R){
swap(arr,L+rand()%(R-L+1),R);/*选择一个随机数作为最后一个数
这也是为什么平均时间复杂度能达到 O(logN)的原因 */
int *p=partition(arr,L,R);//之前函数的返回值,也就是我们分割数组三部分的边界线
quick_sort(arr,L,p[0]-1);//对小于区进行快排
quick_sort(arr,p[0+1],R);//对大于区进行快排
}
}
int *partition(int *arr,int L,int R){//数组、左边界下标、右边界下标
int little_border=L-1;//小于区右边界
int big_boder=R;//大于区左边界
while(L<big_boder){//从头遍历到尾
if(arr[L]<arr[R]){//当前数比选定数小
swap(arr,++little_border,L++);//放在小于区里面,小于区范围++,进入下一个数
}
else if(arr[L]>arr[R]){//当前数比选定数大
swap(arr,--big_boder,L);//放在大于区里面,大于区范围++,停在当前位置
}
else{
L++;//放着不动,相当于等于区,进入下一个数
}
}
swap(arr,big_boder,R);//最后把选定的数放在等于区
int *res=(int *)malloc(sizeof(int)*2);//最后返回一个数组
res[0]=little_border+1;//数组成员有等于区的左边界
res[1]=big_boder;//还有等于区的右边界
return res;//函数返回这个数组的指针,方便我们进行下一次的递归快排
}
int main(){
int arr[100]={0};
int len=0;
while(1){
scanf("%d",&arr[len]);
len++;
if(getchar()=='\n'){
break;
}
}
quick_sort(arr,0,len-1);
for(int i=0;i<len;i++){
printf("%d ",arr[i]);
}
}
测试结果