时间限制 | 内存限制 |
---|---|
1000 ms |
65536 KB |
# 题目描述
在一个由 个元素组成的集合中,第 个顺序统计量是指该集合中第 小的元素,下面介绍一种期望为线性时间的顺序统计量算法。
先给出顺序统计量函数 SELECT(A,p,r,i)
的伪代码,它返回数组 中第 小的元素:
SELECT( A, p, r, i) | |
1 if p == r | |
2 return A[p] | |
3 q = PARTITION( A, p, r) | |
4 k = q - p + 1 | |
5 if i == k | |
6 return A[q] | |
7 else if i < k | |
8 return SELECT( A, p, q-1, i) | |
9 else | |
10 return SELECT( A, q+1, r, i-k) |
函数的第一行检查递归的基本情况,即 中只包含一个元素,在这种情况下 必然等于 ,直接返回 ;第三行的 PARTITION(A,p,r)
自动将数组分为 和 两部分,并返回分界元素的下标 ,其中 中的每个元素都小于或等于 , 中的每个元素都大于 ;第四行计算子数组 中的元素个数 ,如果 等于 ,那就说明 就是 中第 小的元素,直接返回;第七行中,如果 小于 ,说明第 小的元素在前一半区间中,数组 中第 小的元素就是数组 中第 小的元素;第九行中,否则,说明第 小的元素在后一半区间中,数组 中第 小的元素就是数组 中第 小的元素(因为保证了前一半数组中的元素和 都比后一半数组中的元素小)。
下面再给出 PARTITION(A,p,r)
一种实现的伪代码,其作用是用元素 将数组 分为左右两半,左面一半中的元素都小于 ,右面一半中的元素都大于 ,最后再把元素 放在它们中间,并返回 在分割完的数组中的位置,以达到划分的效果:
PARTITION( A, p, r) | |
1 x = A[r] | |
2 i = p - 1 | |
3 for j = p to r - 1 | |
4 if A[j] <= x | |
5 i = i + 1 | |
6 exchange A[i] with A[j] | |
7 exchange A[i+1] with A[r] | |
8 return i + 1 |
下面请你根据上面的伪代码实现顺序统计量函数,注意上面的实现只是期望时间为线性,对于一些特殊的输入最坏情况下算法的复杂度为平方级,在此我们保证对于我们的测试数据,对照上面的伪代码实现就能通过。
# 输入格式
第一行两个正整数 ,分别数组中有 个数,找到第 小的元素,保证 ,。
第二行 个整数,第 个正整数即为数组的第 个元素 ,保证 ,且所有元素互不重复。
# 输出格式
一个整数,表示原数组中第 小的元素。
# 输入样例
5 3 | |
-5 -7 8 -6 4 |
# 输出样例
-5 |
# 题解:快速排序 + 模拟
这个题直接给出了这个算法的伪代码,所以写起来非常好写。其实这个算法就是 C++
中的 nth_element()
函数的原型,该函数就是在线性的时间复杂度内求出数组的第 大 / 小数。关于时间复杂度的证明:
对于随机数据,期望每经过一次 PARTITION
操作后都能将数组对半分,每次 PARTITION
操作的复杂度为区间长度,因此 ,因此时间复杂度期望是线性的。
参考代码:
#include <stdio.h> | |
int n, k, a[4000010]; | |
void Swap(int *a, int *b) | |
{ | |
int t = *a; | |
*a = *b; | |
*b = t; | |
return; | |
} | |
int Partition(int a[], int p, int r) | |
{ | |
int x = a[r], i = p - 1; | |
for (int j = p; j < r; ++ j) | |
if (a[j] <= x) | |
Swap(&a[ ++ i], &a[j]); | |
Swap(&a[i + 1], &a[r]); | |
return i + 1; | |
} | |
int Select(int a[], int p, int r, int i) | |
{ | |
if (p == r) | |
return a[p]; | |
int q = Partition(a, p, r); | |
int k = q - p + 1; | |
if (i == k) | |
return a[q]; | |
else if (i < k) | |
return Select(a, p, q - 1, i); | |
else | |
return Select(a, q + 1, r, i - k); | |
} | |
int main() | |
{ | |
// freopen("H.in", "r", stdin); | |
// freopen("H.out", "w", stdout); | |
scanf("%d%d", &n, &k); | |
for (int i = 1; i <= n; ++ i) | |
scanf("%d", &a[i]); | |
printf("%d\n", Select(a, 1, n, k)); | |
return 0; | |
} |