快速排序模板

快排核心问题

  1. 快排最关键的位置就是 关键点的选取和递归排序时子区间端点的选择,可以简记为

关键点选左端点,子区间端点要看右指针
关键点选右端点,子区间端点要看左指针
关键点选中间,子区间端点随意选
原因可以理解为处理特殊情况(特例见代码)

  1. 在分子区间端点时,为什么是([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;
}
原文地址:https://www.cnblogs.com/G-H-Y/p/14169742.html