快速排序模板
快排核心问题
- 快排最关键的位置就是 关键点的选取和递归排序时子区间端点的选择,可以简记为
关键点选左端点,子区间端点要看右指针
关键点选右端点,子区间端点要看左指针
关键点选中间,子区间端点随意选
原因可以理解为处理特殊情况(特例见代码)
- 在分子区间端点时,为什么是\([l, j], [j + 1, r]\) 和 \([l, i - 1], [i, r]\)
首先要明确我们的目标是保证关键点左侧都是 <= 它的数,关键点右侧都是 >= 它的数。
我们看j点,最终指向的应该是 <= 关键点的数据,由于无法区分是小于还是等于,所以直接分到左半边,所以是\([l, j]\),自然就对应了\([j + 1, r]\)
同理可得\(i\)这种划分方法的原因
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e6 + 10;
int n;
int a[N];
void quick_sort(int a[], int l, int r)
{
if (l >= r) return ;
int x = a[l + r >> 1], i = l - 1, j = r + 1;
while(i < j)
{
while (a[++ i] < x); // 按照实际思路,x左侧应该为<=x的值,但是这么做相当于是<x的值,这样说就说不通了,但是如果写为<=假设对于14253是无法得到正确结果的,这里的边界情况较多,不做深入追究
while (a[-- j] > x);
if (i < j) swap(a[i], a[j]);
}
// 测试x的选取的数据可以是:
// 2
// 1 2
// x = a[l]; || x = a[l + r >> 1];
quick_sort(a, l, j);
quick_sort(a, j + 1, r);
// x = a[r]; || x = a[l + r >> 1];
// quick_sort(a, l, i - 1);
// quick_sort(a, i, r);
}
int main()
{
cin >> n;
for (int i = 0; i < n; ++ i) cin >> a[i];
quick_sort(a, 0, n - 1);
for (int i = 0; i < n; ++ i) cout << a[i] << " ";
return 0;
}
快速排序引申出的快速选择算法
该算法能够在\(O(n)\)的时间复杂度内找到无序序列中第\(k\)小的数
问题描述
给定一个长度为\(n\)的整数数列,以及一个整数\(k\),请用快速选择算法求出数列从小到大排序后的第\(k\)个数
问题分析
已知一个无序序列,我们选定\(x\)作为分界值,快速排序第一轮之后,序列被分为两半部分,左半边所有数值\(<=x\),设长度为\(sl\);右半边所有数值\(>=x\),设长度为\(sr\)
假设我们想要找得是第\(k\)小的数,如果\(k<=sl\),说明待求值位于左半边,如果\(k>sl\),说明待求值位于右半边。根据该性质,我们只需要递归对应区间进行快速排序,最终即可找到第\(k\)小的数
第1轮快速排序运算次数为\(n\),之后每次快排的期望运算次数依次减半,所以\(时间复杂度 = n + \frac{n}{2} + \frac{n}{4} + \ ... \ = \ n(1 + \frac{1}{2} + \frac{1}{4} + \ ...) <= 2n\),所以时间复杂度为\(O(n)\)
代码实现-手写快速选择
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int n, k;
int a[N];
int quick_sort(int a[], int l, int r, int k)
{
if (l >= r) return a[l];
int x = a[l + r >> 1], i = l - 1, j = r + 1;
while (i < j)
{
while (a[++ i] < x);
while (a[-- j] > x);
if (i < j) swap(a[i], a[j]);
}
int sl = j - l + 1;
if (k <= sl) return quick_sort(a, l, j, k);
return quick_sort(a, j + 1, r, k - sl);
}
int main()
{
cin >> n >> k;
for (int i = 0; i < n; ++ i) cin >> a[i];
cout << quick_sort(a, 0, n - 1, k) << endl;
return 0;
}
代码实现-库函数
在algorithm头文件中存在一个nth_element的函数,同样能够找到第k个元素
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int n, k;
int a[N];
int main()
{
cin >> n >> k;
for (int i = 0; i < n; ++ i) cin >> a[i];
nth_element(a, a + k - 1, a + n); // 第k小的值下标为k-1
cout << a[k - 1] << endl; // 第k小的值下标为k-1
return 0;
}