“对于包含n个数的输入数组来说,快速排序是一种最坏情况时间为O(n^2)的排序算法,虽然最坏情况的时间复杂度很差,但是快速排序通常是实际排序应用中最好的选择”-《算法导论》第七章
这篇文章将简单地描述快速排序的思路,代码实现。主要针对的是刚刚学习编程的新人,不是很能理解各种专业术语,被一些博客的高深劝退的人。这篇文章的语言很通俗易懂,代码也很简单
一 快速排序的思想
快速排序实际上运用到了分治的策略,要对一个无序数组进行排序。我们先可以从最简单的情况考虑,如果存在一个数组的有三个元素:a,b,c ,且a>b>c。要从小到大排序,我们找到b为中间值,就只需调换a,c的顺序。 面对一个更复杂的数组,我们也可以用同样的策略:选取该数组中的一个元素q,将小于q的元素放在q的左边,将大于q的元素放在q的右边。并用递归再对左边和右边数组进行再一次分治,重复进行直到整个数组都被排序,我们的排序就完成了。
所以我们的递归思路很简单,就是找到q后,对q左边右边再此调用排序函数,递归的终止条件就是我们进行分治的区间只剩两个元素了。
现在比较关键的是如何分治的问题。这里我们用了一个双指针的思想。我们可以通过两个指针来框出一个区间,这个区间的元素都是大于q的元素,我们不断把大于q的元素放入此区间,把小于q的元素放到此区间的左侧。一直到一边的指针碰到边界。
实现这一分治策略的方法如下:我们的右指针不断向右移动,在这个过程中,如果经过的元素比q大,那我要框出的区间就应该扩张,因此我的左指针不动。但如果经过的元素比q小,则我的区间应保持不变,因此左指针随右指针向右移动。那如何把小于q的元素放在左边呢,很简单:我们只需要交换此时这个区间最右边的元素与最左边的交换,就实现了小于q的在左,大于q的在右的需求。
接下来,我们考虑q的选取,因为对于q值不同的情况,这个排序需要执行的次数不一定,我们这里直接可以选取这个数组中最后一个元素,方便我们直接在数组上调换位置。当然,对元素的选取随机化,有助于保证我们的平均复杂度到较好的水平。
到这里,快速排序的算法就已经清楚了。需要注意的是,我们需要将最后一个元素,即我们选取的q元素,放回数组中间。
下图是此算法的一个演示
二 代码实现
//快速排序
#include <iostream>
#include <algorithm>
using namespace std;
int A[10000];
int n;
int partition(int l, int r) {
int x = A[r];//x即我们选取分隔数组的标准值
int i = l - 1, j = l;//左指针和右指针
while (j <= r - 1) {
if (A[j] <= x) {
i = i + 1;//区间长度不变
swap(A[i], A[j]);//将小于x的元素放在左边
}
j++;
}
swap(A[i + 1], A[r]);//处理我们的x值
return i + 1;
}
void quick_sort(int l, int r) {
if (l < r) {//递归跳出条件
int q = partition(l, r);
quick_sort(l, q - 1);
quick_sort(q + 1, r);
} else
return;
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
scanf("%d", &A[i]);
}
quick_sort(1, n);
for (int i = 1; i <= n; i++) {
printf("%d ", A[i]);
}
return 0;
}
完成!!!