和归并排序一样,快速排序也是基于分治法的。下面是对一个典型的子数组A[p..r]排序的分治过程的步骤:
1.分解:将数组数组A[p..r]划分成两个(可能为空)子数组A[p..q-1]和A[q+1..r],使得A[p..q-1]中的每一个元素都小于A(q),且A[q+1..r]大于等于元素A(q)。总之,就是以A[q]为分界点进行划分,具体的步骤稍后说明。
2.解决:通过递归调用快速排序算法,对A[p..q-1]和A[q+1..r]进行排序。
3.合并:由于两个子数组就地排序(就是不需要额外的空间),所以合并不需要任何操作
一些具体的实现过程可以参考代码上的注释:
#include <stdio.h>
#include <stdlib.h>
//测试的数组的最大数目为MAX
#define MAX 10
//分割的过程,这里是将A[r]也就是最后一个元素作为比较的值
//想象一下,有两个指针i和j,一开始i指向第一个元素之前,而j指向i元素下一个
//然后j开始向右扫描,只要j指向的元素是大于A[r]的,那么j继续前进,此时i和j之间的元素必然全部大于A[r]
//而一旦出现j指向的元素小于A[r],这时候只需要将i的值加1,然后A[i]与A[j]互换,那么i的值左边的元素必然全部小于A[r]
//全部扫描完成后,i就成为了分割点,这时候只需要将i+1的值返回就可以了
int Partition(int* A,int p,int r)
{
int x=A[r];
int i=p-1;
int j,temp;
for(j=p;j<r;j++)
{
if(A[j]<=x)
{
i++;
temp=A[i];A[i]=A[j];A[j]=temp;
}
}
temp=A[i+1];A[i+1]=A[r];A[r]=temp;
return i+1;
}
//快排的主算法,得到分割点后递归调用自身就行了
void Quick_Sort(int* A,int p,int r)
{
int q;
if(p<r)
{
q=Partition(A,p,r);
Quick_Sort(A,p,q-1);
Quick_Sort(A,q+1,r);
}
}
//主程序进行数据测试
int main()
{
int A[MAX]={34,6,2,56,34,3,22,56,4,1};
Quick_Sort(A,0,9);
int count;
for(count=0;count<MAX;count++)
printf("%d ",A[count]);
printf("\n");
return 0;
}
当然了,使用这种方式得到的快排算法并不是最优的,当给定的数组一开始就是排好序的或是逆序排好的,那么算法的时间复杂度将和冒泡排序一样差,解决的方法就是使用随机化算法,这里由于每次选择比较的元素都是A[r],也就是最后一个元素,而随机化的方法就是是比较的元素不确定化,将A[r]通过rand()或者其他什么随机函数进行选取,那么,算法的时间复杂度将不会取决于输入的有序程度(这就是概率论的知识了,我就不做详细说明了~)。