快速排序是对冒泡排序的改进,基本思想是基于分治法来减少交换次数
基本步骤
1、先找一个基准元素,可以是第一个元素(但这样可能造成复杂度提升,在后面基准点的取法会讲到)
2、将小于基准元素的数都移动到它的左边,大于基准元素的数都移动到它的右边。此时,基准元素位于它最终的位置
3、对基准元素左右两个分块重复以上的步骤,直到全部有序。
时间复杂度
如果每次能够将数组划分为两个区间,总共需要进行 logn 趟排序,所以平均复杂度为 O(nlogn)。
极端情况下每次只能将数组划分为一个区间,这样的话就需要进行 n 趟排序,此时最坏的时间复杂度为 O(n^2)。
空间复杂度
因为快速排序是递归的形式,如果每次能够将数组划分为两个区间,会递归 logn 层,此时平均复杂度为 O(logn)。
最坏情况下会递归 n 层,所以最坏复杂度为 O(n)。
基准点的选取
方法1:选取第一个元素作为基准点。
当数组基本有序或基本逆序时,时间复杂度会退化为 O(n^2)
方法2:使用中间点作为基准点
可以每次选取 arr[l + r >> 1] 作为基准点
代码模板
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
void quick_sort(int q[], int l, int r)
{
if (l >= r) return;
int i = l - 1, j = r + 1, x = q[l + r >> 1];
while (i < j)
{
do i++ ; while (q[i] < x);
do j-- ; while (q[j] > x);
if (i < j) swap(q[i], q[j]);
}
quick_sort(q, l, j), quick_sort(q, j + 1, r);
}
int main(void)
{
int idx = 0;
int a[15];
// 用时间作为种子生成随机数
srand(time(0));
cout << "随机生成的数字:";
for (int i = 0; i < 10; i++) {
a[idx++] = rand();
cout << a[idx - 1] << " ";
}
cout << endl;
quick_sort(a, 0, 9);
cout << "排序后的数字:";
for (int i = 0; i < 10; i++) {
cout << a[i] << " ";
}
cout << endl;
return 0;
}
P1177 【模板】快速排序
题目链接:https://www.luogu.com.cn/problem/P1177
对于这道题,如果每次取第一个元素(即令 x = q[l]),会有两组数据TLE。因为如果数组是有序或者逆序的,那么复杂度会退化为 O(n^2)
基准点取中间元素 x = q[l + r >> 1] 是一种很好的方法,能过掉所有数据
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int a[N];
void quick_sort(int q[], int l, int r)
{
if (l >= r) return;
int i = l - 1, j = r + 1, x = q[l + r >> 1];
while (i < j)
{
do i++ ; while (q[i] < x);
do j-- ; while (q[j] > x);
if (i < j) swap(q[i], q[j]);
}
quick_sort(q, l, j), quick_sort(q, j + 1, r);
}
int main(void)
{
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
quick_sort(a, 0, n - 1);
for (int i = 0; i < n - 1; i++) {
printf("%d ", a[i]);
}
printf("%d\n", a[n - 1]);
return 0;
}