P1923. 模板题_求第 k 小的数

考点

  • 排序

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <bits/stdc++.h>

using namespace std;

const int LEN = 5e6 + 10;

int K, arr[LEN];

int k_th(int arr[], int len)
{
if (len <= 1)
return arr[0];
int i = 0, j = 0, k = len, pivot = arr[rand() % len];
while (i < k)
{
if (arr[i] < pivot)
swap(arr[i++], arr[j++]);
else if (arr[i] > pivot)
swap(arr[i], arr[--k]);
else
++i;
}
if (K < j)
return k_th(arr, j);
else if (K >= k)
{
K -= k;
return k_th(arr + k, len - k);
}
else
return pivot;
}

int main()
{
//这行必须加,加快cin的读取速度
ios::sync_with_stdio(false);
int n;
cin >> n >> K;
for (int i = 0; i < n; ++i)
cin >> arr[i];
cout << k_th(arr, n);
return 0;
}

思路

不同题目对第K大第K小都有不同的定义;就本题而言,是求升序数列中的第K个元素,K从0开始

可以联想到三路快排算法:每次都以pivot为标准,左半部分均小于pivot,中间部分等于pivot,右半部分大于pivot

所以只需要判断K处在哪一个部分:

  • K在左半部分,向左半部分递归
  • K在中间部分,直接返回结果,因为中间部分的值都是相同的
  • K在右半部分,K先减去左半部分加中间部分的长度,以转换为右半部分子数组的下标,再向右半部分递归