【AcWing 786. 第k个数】题解

【AcWing 786. 第k个数】

快速选择板子:【排序板子】在【1.1.3 快速选择】

本题是快速选择算法,或随机选择算法。

对区间 [ l , r ] [l, r] [l,r] 进行一次快速排序后,分界点 n u m s [ x ] nums[x] nums[x] 左侧的元素都小于等于分界点,分界点 n u m s [ x ] nums[x] nums[x] 右侧的元素都大于分界点,则分界点 n u m s [ x ] nums[x] nums[x] 就是区间 [ l , r ] [l, r] [l,r] 中第 x − l + 1 x-l+1 xl+1 大的数,因此:

  • k = x − l + 1 k=x-l+1 k=xl+1 时,第 k k k 大的数就是分界点 n u m s [ x ] nums[x] nums[x]
  • k < x − l + 1 kk<xl+1 时,第 k k k 大的数在分界点 n u m s [ x ] nums[x] nums[x] 的左侧,向左侧区间递归;
  • k > x − l + 1 k>x-l+1 k>xl+1 时,第 k k k 大的数在分界点 n u m s [ x ] nums[x] nums[x] 的右侧,向右侧区间递归;
  • 因为递归边界是 l > = r l>=r l>=r ,则返回 n u m s [ l ] nums[l] nums[l]

随机选择只需要把随机快排的步骤迁移过来即可。

C++

#include 
using namespace std;
const int N = 100010;
int n, k, nums[N];

int quickSelect(int l, int r, int k) 
{
    if (l >= r) return nums[l];
    int i = l - 1, j = r + 1, x = nums[l + r >> 1];
    while (i < j) {
        do ++ i; while (nums[i] < x);
        do -- j; while (nums[j] > x);
        if (i < j)  swap(nums[i], nums[j]);
    }                                                      // 一次快排完成后,分界点x就是区间里第j-l+1大的元素
    if (k <= j - l + 1) return quickSelect(l, j, k);       // 如果k<=j-l+1,第k大的数在x左侧,向左递归
    else    return quickSelect(j + 1, r, k - (j - l + 1)); // 如果k>j-l+1,第k大的数在x右侧,向右递归
}

int main()
{
    scanf("%d %d", &n, &k);
    for (auto i = 0; i < n; ++ i)   scanf("%d", &nums[i]);
    printf("%d", quickSelect(0, n - 1, k));
    return 0;
}

你可能感兴趣的:(AcWing题解,算法,数据结构,c语言)