先给出个人认为在众多由不同的划分过程导致的各种快排算法中最高效简洁的算法:
void Qsort(int *A,int x,int y)
{
if(x>=y) return;
int p=x,q=y,m=A[x];
do
{
while(A[p]<m) p++;
while(A[q]>m) q--;
if(p<=q) swap(&A[p++],&A[q--]);
}while(p<q);
Qsort(A,x,q);
Qsort(A,p,y);
}
其次,给出这个算法的分析:
void Qsort(int *A,int x,int y)
{
if(x>=y) return;
int p=x,q=y,m=A[x];
do{
while(A[p]<m) p++;//在左边找到不小于m的元素
while(A[q]>m) q--;//在右边找到不大于m的元素
if(p<=q)//在自加自减之前,使左边A[p]不大于m,右边A[q]不小于m。
{
swap(&A[p++],&A[q--]);//考虑在这一步之后,p>q的情形。则在自加自减之前,有两种情况,1)q-p=1,则退出时,p-q=1,也即pq换成了qp的顺序,此时,[q+1,y]中的元素一定不小于m,[x,p-1]一定不大于m,此时再对而p=q+1,q=p-1,所以此时再对[x,q],[p,y]排序即可
//print(A,n);//2)在自加自减之前,p==q,显然A[p]=A[q]=m,之后的顺序是qop,也即q与p隔着一个位置,此时,[x,q]的元素均不大于m,[p,y]中的元素均不小于m
}//合并一下,即对[x,q],[p,y]分别排序即可
}while(p<q);
//printf("p=%d q=%d\n",p,q);
Qsort(A,x,q);
Qsort(A,p,y);
}
如果,你觉得上面的算法难以理解划分过程的细节,如什么时候p<q,为什么A[p]<m,而不是A[p]<=m,试试理解这个,我认为这个在理解上是最简单的:
void qsort(int *A,int x,int y)//[x,y]
{
if(x>=y) return;
int p=x,q=y,m=A[x];
for(;;)
{
while(A[q]>m) q--;//找到右边第一个不大于m的元素
if(q==p) break;
swap(&A[p++],&A[q]);//把左边当前元素与右边不大于m的元素交换,保证左边当前元素不大于m
while(A[p]<m) p++;//在左边找到不小于m的元素,在第一轮的检查中,A[q]此时为m,最极端情况是在p==q时,一定会终止循环
if(p==q) break;//此时终止循环,左边的元素都不大于m,右边的元素都不小于m,中间的元素为m
swap(&A[p],&A[q--]);//把m移到左边p处,运行后,原来q所在处的元素一定大于等于m
}
qsort(A,x,p-1);//对左边不大于m的所有元素排序
qsort(A,q+1,y);//对右边不小于m的所有元素排序
}
以下算法也容易理解,而且不需要swap函数:
void Qsort(int *A,int x,int y)
{
if(x>=y) return;//递归边界
int p=x,q=y,m=A[x];//把A[x]设为标兵,同时把此位置看做空位
do
{
while(p<q && A[q]>=m)//找到右侧第一个小于m的元素
q--;
if(p<q) A[p++]=A[q];//移到空位,此时空位位置上的元素小于m,,加入判断条件(p<q)是因为要确保上一循环语句是因为找到元素而退出的
while(p<q && A[p]<=m)//找到左侧第一个大于m的元素
p++;
if(p<q) A[q--]=A[p];//将找到的大于m的元素移到空位,加入判断条件(p<q)是因为要确保上一循环语句是因为找到元素而退出的
}while(p<q);//每次对p,q的值仅仅加1或减1,所以最后必定是p==q而退出的
A[p]=m;//放回空位
Qsort(A,x,p-1);
Qsort(A,p+1,y);
}
给出其中一个完整测试程序:
#include<stdio.h>
#include<string.h>
#include<time.h>
const int maxn=5000000;
void swap(int *a,int *b)
{
int t=*a;*a=*b;*b=t;
}
void qsort(int *A,int x,int y)//[x,y]
{
if(x>=y) return;
int p=x,q=y,m=A[x];
for(;;)
{
while(A[q]>m) q--;//找到右边第一个不大于m的元素
if(q==p) break;
swap(&A[p++],&A[q]);//把左边当前元素与右边不大于m的元素交换,保证左边当前元素不大于m
while(A[p]<m) p++;//在左边找到不小于m的元素,在第一轮的检查中,A[q]此时为m,最极端情况是在p==q时,一定会终止循环
if(p==q) break;//此时终止循环,左边的元素都不大于m,右边的元素都不小于m,中间的元素为m
swap(&A[p],&A[q--]);//把m移到左边p处,运行后,原来q所在处的元素一定大于等于m
}
qsort(A,x,p-1);//对左边不大于m的所有元素排序
qsort(A,q+1,y);//对右边不小于m的所有元素排序
}
int A[maxn];
void print(int *A,int n)
{
printf("%d",A[0]);
for(int i=1;i<n;i++)
printf(" %d",A[i]);
printf("\n");
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int n;
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&A[i]);
//print(A,n);
qsort(A,0,n-1);
print(A,n);
//printf("Time used=%.4lf\n",(double)clock()/CLOCKS_PER_SEC);
return 0;
}
测试样例:
10
-70 -94 231 -404 -318 -203 99 99 -383 175