很大的数组的第k小(快速排序)

Description
求数组的第k小,数字数量非常多。

Input
每组数据给出n m k表示有n个数,求第k小,数组的数字由以下规则得到:

ai = mi mod  (109+7), i = 1, 2, ..., n

其中 1 ≤ n, m ≤ 5 × 107, 1 ≤ k ≤ n,数据保证得到的数组元素大部分互不相等。

Output
输出第k小的数

Sample Input
3 2 2
Sample Output
4

思路上第一可能会先想到的是:先对数组进行排序后再得到第k个数即可

#include<cstdio>

using namespace std;

const int mod=1e9+7;
const int MAX=5e7+10;
int arr[MAX];
int n,m,k;

void quickSort(int left,int right){//左闭右闭 
	if(left>right){
		return ;
	}
	int temp=arr[left];//存储对照点
	int i=left,j=right;
	while(i<j){//从右边开始找一个小于对照点的数 
		while(arr[j]>=temp&&i<j){
			j--;
		}
		//从左边开始找一个大于对照点的数
		while(arr[i]<=temp&&i<j){
			i++;
		} 
		//找到后交换两个数
		if(i<j){
			int num=arr[i];
			arr[i]=arr[j];
			arr[j]=num;
		} 
	}
	//退出循环后i=j的位置就是temp的位置
	arr[left]=arr[i];
	arr[i]=temp; 
	quickSort(left,i-1);//递归左边 
	quickSort(i+1,right);//递归右边 
}

int main(){
	scanf("%d%d%d",&n,&m,&k);
	arr[0]=m;
	for(int i=1;i<=n-1;i++){
		arr[i] = 1LL * arr[i - 1] * m % mod;//利用前缀和读入
	}
	quickSort(0,n-1);
//	for(int i=0;i<n;i++){
//		printf("%d ",arr[i]);
//	}
	printf("%d\n",arr[k-1]);
	return 0;
} 

但是面对大量数据时,该算法会出现超时

因此我们需要优化,这里介绍使用快速排序来得到数组第k小数字的算法

我们知道快速排序每次都会选定一个 对照值 然后进行分区,然后将比对照值小的数放到对照值的左边,将比对照值大的数放到对照值的右边,此后该对照值的位置就不会再改变了

因此利用这一点,我们可以加以改造来求第k小的数:

如果说我们得到的对照值下标为index(下标从0开始),如果index+1刚好就等于k,说明了该对照值就是第k小个数

如果我们得到的index+1>k的话,则该对照值必然不会是第K小的数,当然,在该对照值右边的就更加不是了,因此我们可以将范围缩小到该对照值的左边

如果我们得到的index+1<k的话,则该对照值也不会是我们想要的,当然在该对照值左边比它还小的就更不是了,可以将范围缩小到该对照值下标的右边了

#include<cstdio>
#include<vector>
using namespace std;

const int mod=1e9+7;
const int MAX=5e7+10;
int arr[MAX];
int n,m,k;

int quickSort(int left,int right){//左闭右闭 
	int temp=arr[left];//存储对照点
	int i=left,j=right;
	while(i<j){//从右边开始找一个小于对照点的数 
		while(arr[j]>=temp&&i<j){
			j--;
		}
		//从左边开始找一个大于对照点的数
		while(arr[i]<=temp&&i<j){
			i++;
		} 
		//找到后交换两个数
		if(i<j){
			int num=arr[i];
			arr[i]=arr[j];
			arr[j]=num;
		} 
	}
	//退出循环后i=j的位置就是temp的位置
	arr[left]=arr[i];
	arr[i]=temp; 
//	return i;	
//	quickSort(left,i-1);//递归左边 
//	quickSort(i+1,right);//递归右边 
	if(i+1==k){//刚好是第K个
		return arr[i];
	}else if(i+1>k){//太大了
		return quickSort(left,i-1);
	}else{//太小了
		return quickSort(i+1,right);
	}
}


int main(){
	scanf("%d%d%d",&n,&m,&k);
	arr[0]=m;
	for(int i=1;i<=n-1;i++){
		arr[i] = 1LL * arr[i - 1] * m % mod;
	}
	int ans=quickSort(0,n-1);
	printf("%d\n",ans);
	return 0;
} 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZZZWWWFFF_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值