常规快速排序基本思想:
分解-->递归求解
以下是我认为最简洁的快速排序代码
void QuickSort(int a[],int p,int r)
{
if(p<r)
{
int q=Partion(a,p,r);
QuickSort(a,p,q-1);
QuickSort(a,q+1,r);
}
}
int Partion(int a[],int p,int r)
{
int i=p,j=r+1;
int x=a[p];
while(true)
{
while(a[++i]<x&&i<r);//i没有等于r的原因,是为了避免重复比较
while(a[--j]>x);
if(i>=j) break;
Swap(a[i],a[j]);
}
a[p]=a[j]; //至于为什么要交换a[j]和a[p]因为排序到了最后只剩下a[p]大于或者等于a[j](假如只有2,5此时a[p]=a[j]=2)所以必须要交换
a[j]=x;
PrintArray(a,9);
return j; //为什么选择j而不是i,因为到了i>j的情况时,a[i]肯定大于a[j],选择a[i]的话肯定会打乱排序的
}
红色数字表示分割数组的基准元素,左边小于它,右边大于它。
蓝色下划线表示将要进行排序的元素。
总所周知:快速排序的算法平均时间为O(nlogn),因为存在最差的情况O(n^2),这时间复杂度只是平均而已,具体到某个实例或许就不是这样了
为什么呢?
因为快速排序运行时间与划分是否对称有关,假如你的每次划分都不是很均匀,时间复杂度绝对不可能有O(nlogn),
例如(8,7,6,5,4,3,2,1,0),结果如图:
这里可以看出有8次递归,并没有>8log8=7.22,从输出可见,而且有些递归是没有意义的。
那么所谓的均匀划分又是什么呢?
在某一轮排序时,不是单一只选择首位做基准(如上述的常规算法),而是每次基准的选择都是随机的,对每个元素而言概率大致相同的。
int Partion(int a[],int p,int r)
{
int i=p,j=r+1;
<span style="color:#FF0000;">int k=Random(p,r);
Swap(a[p],a[k]);</span>
int x=a[p];
while(true)
{
while(a[++i]<x&&i<r); //i没有等于r的原因,是为了避免重复比较
while(a[--j]>x);
if(i>=j) break;
Swap(a[i],a[j]);
}
a[p]=a[j]; //至于为什么要交换a[j]和a[p]因为排序到了最后只剩下a[p]大于或者等于a[j](假如只有2,5此时a[p]=a[j]=2)所以必须要交换
a[j]=x;
PrintArray(a,9);
return j; //为什么选择j而不是i,因为到了i>j的情况时,a[i]肯定大于a[j],选择a[i]的话肯定会打乱排序的
}
int Random(int a,int b)
{
return (a+b)/2;
}
在这里我的随机生成数函数是简单的取中位数,但确实是有效果的,递归规模降低成了7次
然后当我优化一下随机数生成函数,效果更加明显,递归次数基本上4,5,6左右
int Random(int a,int b)
{
srand((int)time(NULL));
return (int)(rand()%(b-a+1)+a);
}
看看激动不,当时我就震惊了,时间复杂度竟然可以优化大O(n/2)啊,简直是超神了。
由此可见快速排序的时间复杂度和划分程度有很大关系,更重要的是随机选择策略的使用,会显著提高快排的速度。