题目描述
给定一个长度为n(1≤n≤1,000,000)的无序正整数序列,以及另一个数k(1≤k≤1,000,000)(关于第k大的数:例如序列{1,2,3,4,5,6}中第3大的数是4。)
输入
第一行两个正整数m,n。
第二行为n个正整数。
输出
第k大的数。
样例输入
6 3
1 2 3 4 5 6
样例输出
4
代码展示
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn = 1000010;
int a[maxn]; // a存放所有整数
// 选取随机主元,对区间[left,right]进行划分
int randPartition(int a[], int left, int right)
{
int p = round(1.0 * rand() / RAND_MAX * (right - left) + left);
swap(a[p], a[left]);
// 以下是划分过程
int temp = a[left];
while(left < right)
{
while(left < right && a[right] > temp)
right--;
a[left] = a[right];
while(left < right && a[left] <= temp)
left++;
a[right] = a[left];
}
a[left] = temp;
return left;
}
// 随机选择算法,从a[left,right]中返回第k大的数
int randSelect(int a[], int left, int right, int k)
{
if(left == right)
return a[left]; // 边界
int p = randPartition(a, left, right); // 划分后的主元位置为p
int m = right - p + 1; // a[p]是a[left,right]中的第m大
if(k == m)
return a[p];
if(k > m)
return randSelect(a, left, p - 1, k - m);
else
return randSelect(a, p + 1, right, k);
}
int main()
{
srand((unsigned)time(NULL)); // 初始化随机数种子
int n, m;
scanf("%d%d", &n, &m);
for(int i = 0; i < n; i++)
scanf("%d", &a[i]);
printf("%d", randSelect(a, 0, n - 1, m));
return 0;
}
小结
出现了两个错误分享一下:
- 开辟大数组时应放在main函数之前,即声明一个全局数组。全局变量在静态存储区分配内存,局部变量是在栈上分配内存空间,一般堆栈默认是1M,int a[1000000]的大小是4*1000000,将近4M,远远大于1M,编译连接的时候不会有问题,但运行时堆栈溢出,程序异常终止。
- 函数的作用域只是从定义的地方开始的。也就是说,在函数定义行之上,你是无法使用该函数的。
ps:
在算法笔记这本书中149页随机选择算法应改为求第k小的数。求第k大的数只需要修改randSelect函数(参照上面的代码)。