流程
- 定义一个初始基准元素,来把序列划分成两部分,其中左边部分全部比基准元素小,右边部分全部比基准元素大。这个基准元素从序列里找,一般找左边第一个元素。
- 定义两个指针,分别指向序列的头和尾,然后开始向中间移动。其中左指针查找比基准元素大的值,放到队列的右边;右指针查找比基准元素小的值,放到队列的左边。如此一来,一趟遍历过后,可以确定到基准元素的位置
- 基准元素确定后,就相当于一个挡板,把序列分成两段,然后这时,再对这两段分别进行一次快速排序(重复1、2步)。直到左指针大于等于右指针。
(可以进行随机化优化。
演示
输入序列为:3 9 5 4 1
编号 | 执行操作前的a[] | 操作 | |
---|---|---|---|
0. | 1~5 | 3 9 5 4 1 | 基准为3, i = 2 , j = 5 。 a [ i ] > 3 , a [ j ] < 3 i=2,j=5。a[i]>3,a[j]<3 i=2,j=5。a[i]>3,a[j]<3,交换a[2]和a[5]。 |
1. | 3 1 5 4 9 | 基准为3,j向左寻找小于基准的元素。 | |
2. | 3 1 5 4 9 | i==j,寻找结束。i前所有元素(除了基准自己)均小于3,i后所有元素均大于3。交换基准与a[i]。 | |
3. | 1 3 5 4 9 | 对基准左侧元素进行排序。 | |
4. | 1 | 1 | 返回有序子数组1。 |
5. | 1~5 | 1 3 5 4 9 | 对基准右侧元素进行排序。 |
6. | 3~5 | 5 4 9 | 基准为5,a[j]>5,j向左寻找小于基准的元素。 |
7. | 5 4 9 | i==j,寻找结束。i前所有元素(除了基准自己)均小于5,i后所有元素均大于5。交换基准与a[i]。 | |
8. | 4 5 9 | 对基准左侧元素进行排序。 | |
9. | 3 | 4 | 返回有序子数组4。 |
10. | 3~5 | 4 5 9 | 对基准右侧元素进行排序。 |
11. | 5 | 9 | 返回有序子数组9。 |
12. | 3~5 | 4 5 9 | 返回有序子数组4 5 9。 |
13. | 结束 | 1 3 4 5 9 |
代码
#include <stdio.h>
#define N 100001
int n,a[N];
void paixu(int l,int r)
{
if (l>=r) return;
int i,j;
for (i=l,j=r;i<j;)
{
while (i<j&&a[j]>a[l]) j--;
if (i>=j) break;
while (i<j&&a[i]<=a[l]) i++;
if (i>=j) break;
a[i]+=a[j],a[j]=a[i]-a[j],a[i]-=a[j];
}
int t=a[l];a[l]=a[i];a[i]=t;
paixu(l,i-1);
paixu(i+1,r);
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;++i) scanf("%d",&a[i]);
paixu(1,n);
for (int i=1;i<=n;++i) printf("%d ",a[i]);
}
时间复杂度
最优情况下,快速排序每次将序列分为等长的两组,则时间复杂度证明同归并排序,为O(nlogn)。
在最坏情况下,即序列为正序数列或逆序数列时,且每次划分只得到一个比上一次划分少一个元素的子序列和一个空序列,则会共进行n划分,每次划分都会遍历少了一个元素的子序列,时间复杂度为O(n^2)。
平均复杂度O(nlogn)待填。