原理
基于分治算法
步骤
一个q数组,左边界的下标为l,右边界的下标为r
- 确定分界点:x=q[l]或q[(l+r)/2]或q[r]或者一个随机数
- 调整区间,所有小于x的数在左边,大于等于x的数在右边
- 递归处理左右两个区间
实现方法
- 开辟两个新的数组a和b,分别存放小于x的数和大于等于x的数,然后将a、b两个数组合并。
int k=0,p=0;
int a[N],b[N];
int q[N];
int quicksort(int l,int r)
{
x=q[l];
for(int i=l;i<=r;i++)
{
if(q[i]<x)
{
a[k]=q[i];
k++;
}
else
{
b[p]=q[i];
p++;
}
}
for(int i=0;i<k;i++)
{
q[l+i]=a[i];
}
for(int i=0;i<p;i++)
{
q[l+k+i]=a[i];
}
}
-
左边设置一个指针i=l,右边设置一个指针j=r。
i所指向的元素小于x,i++;
i所指向的元素大于等于x,停止移动i,去移动j;
j所指向的元素大于x,j–;
j所指向的元素小于等于x,停止移动j,i所指向的元素和j所指向的元素交换,i++,j–。
直到i和j相遇。例如:3 1 2 3 5
0 1 2 3 4
x=3
i=-1,j=5
i++;i=0,j=5;q[i]=q[0]=3,i停止移动,
j–;i=0,j=4:q[j]=q[4]=5>3,
j–,i=0,j=3;q[j]=q[3]=3=3,j停止移动,
q[i]=q[0]=3和q[j]=q[3]=3交换;
i++;i=1,j=3;q[i]=q[1]=1<3;
i++,i=2,j=3;q[i]=q[2]=2<3;
i++,i=3,j=3;q[i]=q[3]=3=3,i停止移动;
j–;i=3,j=2;q[j]=q[2]=2<3,j停止移动;
i>j,不能再交换了。
i>j,停止循环。
i左边的数(不包括i)都小于等于3,j右边的数(不包括j)都大于等于3。
#include<iostream>
using namespace std;
const int N = 1e6+10;
int n;
int q[N];
void quick_sort(int q[],int l,int r)
{
// 区间里面只有一个数或者没有数,就不用排序了
if(l>=r)
{
return;
}
int x=q[l];//分界点
int i=l-1,j=r+1;// 因为是do while循环
while(i<j)
{
do i++; while (q[i]<x);
do j--; while (q[j]>x);
if(i<j) swap(q[i],q[j]); // swap函数存在于<iostream>文件中
}
// 递归排列左右两边
// 因为左部分小于等于x,右部分大于等于x,所以递归只有两种情况,即sort(l,i-1),sort(i,r);
或者 sort(l,j),sort(j+1,r);
// 因为对于i来说,i的左边的值是确定的,一定小于等于x,(因为i在j的右边,j右边的值都是大于等于x,所以q[i]一定大于等于x,因为q[i]不能确定是否等于x,而i-1一定可以确定小于等于x,所以递归时只能时sort(l,i-1),sort(i,r);
// 因为对于j来说,j的右边的值是确定的,一定大于等于x,(因为j在i的左边,i左边的值都是小于等于x,所以q[j]一定小于等于x,因为q[j]不能确定是否等于x,而j+1一定可以确定大于等于x,所以递归时只能时sort(l,j),sort(j+1,r);
// 当x=q[l]的边界情况:
// 当只有两个元素时,
// 例如 1,2
// 0,1
// x=1
// i=-1,j=2
// i++;i=0,j=2;q[i]=q[0]=1=1,i停止移动
// j--;i=0,j=1;q[j]=q[1]=2>1;
// j--;i=0,j=0;q[j]=q[0]=1,j停止移动
// i=j,q[i]与q[j]不能交换
// i=j,退出while(i<j)的循环,quick_sort(q,l,i-1)=quick_sort(q,0,-1),quick_sort(q,i,r)=quick_sort(q,0,1)
// 这时按照代码执行下来,循环之前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]的边界情况
道理类似,如果此时为2,1也会导致j=r,进行sort(l,j),sort(j+1,r);导致死循环(一直sort(l,r),此时只能用sort(l,i-1),sort(i,r);
// 元素2 1
// 下标0 1
// x=1
// i=-1,j=2
// i++;i=0,j=2;q[i]=q[0]=2>1,i停止移动
// j--;i=0,j=1;q[j]=q[1]=1=1,j停止移动
// q[i]=q[0]与q[j]=q[1]交换
// i++;i=0,j=1;q[i]=q[0]=1=1,i停止移动
// j--;i=0,j=1;q[j]=q[1]=2>1
// j--,i=0,j=0;q[j]=q[0]=1=1,j停止移动
// i=j,q[i]与q[j]不能交换
// i=j,退出循环
// quick_sort(q,l,j)=quick_sort(q,0,1),quick_sort(q,j+1,r)=quick_sort(q,2,1);
// 当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);
// [解释详见](https://blog.csdn.net/xxxxian666/article/details/114818122)
quick_sort(q,l,j);
quick_sort(q,j+1,r);
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&q[i]);
}
quick_sort(q,0,n-1);
for(int i=0;i<n;i++)
{
printf("%d\n",q[i]);
}
return 0;
}
时间复杂度分析