概述
快速排序是对冒泡排序的一种改进,也是当前人们认为综合效率最好的排序算法。
下面,让我们一起来学习最基本的快速排序叭~
建议大家先看看第一篇和第二篇。
算法思路
我们需要先找一个基准数k。在最基本的快速排序中,k可以是序列中的第一个数。
对于左指针,一个元素合法的定义是:这个数≤k。
左指针向后遍历不停地向后遍历,寻找第一个不合法的数。
3, 7, 1 ≤ 8,
10 ≥ 8。
左指针第一个不合法的数就是a[4] = 10。
对于右指针,一个元素合法的定义是:这个数≥k(和k值相等的数在那边都可以)。
在右指针那里,第一个数6就 < 8,不合法。
为了使得这两个数合法,我们把它们交换一下~
我们按照刚才的步骤再操作一次,又得到了两个不合法的数。
交换后,整个序列都合法了。
此时的a数组被分成的两个部分:<k,和≥k的。
我们可以序列分为两部分,每部分继续执行刚才的操作,直到整个序列有序。
动画演示
为方便大家理解,蒟蒻君给大家找了个动画。
这个动画与上述方法唯一不同的就是,基准数放到最后了。但本质还是没变。
代码实现
void quick_sort(int a[], int l, int r) { // l, r表示排序范围
// 递归终止条件:序列为空
if (l > r) {
return;
}
int i = l, j = r, k = a[l];
while (i != j) {
// 一定要先确定右指针,因为这样从而就能确定左指针遍历的范围。
while (a[j] >= k && i < j) { // 右指针如果元素合法就一直向左遍历,停下的时候就是第一个不合法的元素
--j;
}
while (a[i] <= k && i < j) { // 左指针同理
++i;
}
swap(a[i], a[j]); // 即使i == j,交换也不会影响结果
}
// 基准数归位
a[l] = a[i];
a[i] = k;
// 将这一段数分为两段处理
quick_sort(a, l, i - 1); // 继续处理左边的,这里是一个递归的过程
quick_sort(a, i + 1, r); // 继续处理右边的 ,这里是一个递归的过程
}
算法特点
- 每次将序列分成两部分用O(log2n),共分n次,最优/最差/平均时间复杂度O(n log2n)。
- 原地排序,空间复杂度O(1)。
- 因为基准数选择较随意,在快速排序里算比较慢的了(所有种类快速排序本质不会差太多,所以时间复杂度都会写成O(n log2n)。