随机选择算法

寻找第k大的数:

第k大:(partition,基准值左边是小于他的数右边是大于他的数)

代码:

#include<iostream>
#include<stdlib.h>
#include<time.h> 
using namespace std;
int n,k;
const int maxn=100010;
int a[maxn];
int partition(int a[],int left,int right){//找基准值,分成两部分; 
	int num=rand()%(right-left+1)+left;
	int temp=a[num];
	swap(a[num],a[left]);
	while(left<right){
		while(left<right&&temp<a[right])right--;
		a[left]=a[right];
		while(left<right&&a[left]<=temp)left++;
		a[right]=a[left];
	}
	a[left]=temp;
	return left;
}
void  randselect(int a[],int left,int right,int k){
	if(left==right)return ;//边界,找到第k大的数; 
	int p=partition(a,left,right);//基准值的下标; 
	int m=p-left+1;//基准值是数列里面的第m大的数; 
	if(m==k)return ;//如果刚好是基准值;
	if(k<m){
		randselect(a,left,p-1,k);//在左区间找; 
	}else {
		randselect(a,p+1,right,k-m);//在右区间的一个新数列找,新数列的开始是p+1,所以是k-m,即是新数列的第k-m大的数,而不是第k大的数; 
	}
}
int main(){
	srand((unsigned)time(NULL));
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>a[i];
	}
	cin>>k;
	randselect(a,0,n-1,k);
	cout<<a[k-1];
	return 0;
} 

思路:利用基准值把区间分为两部分,大于它的数和小于它的数,用partition可以得到基准值的下标,下标-左边界+1就是在这个区间内,基准值是第(p-left+1)大,如果k(第k大)大于基准值的第(p-left+1)大,令m=p-left+1,就说明k在右区间,就让又区间作为一个左边界为p+1,右边界为right,因为区间改变,所以找的值是k-m不是k,k是原来区间的第k大,新区间的第k大应该是k-m,如果k<m,就说明在左区间,左区间和原区间的起点相同且k<m,所以就是k就不是k-m;

问题二:

一个数列里面,将这个数列尽可能的分成两个相等的部分,且满足两个数列的和的差最大,求这个差

思路:只用求出第n/2大,左边都是小于n/2大的数,右边都是大于n/2的数,不在意顺序,因为是求和,就可以算出前n/2的和s1,后s2,此时s2-s1最大;

代码:

#include<iostream>
#include<stdlib.h>
#include<time.h> 
using namespace std;
int n,k;
const int maxn=100010;
int a[maxn];
int partition(int a[],int left,int right){//找基准值,分成两部分; 
	int num=rand()%(right-left+1)+left;
	int temp=a[num];
	swap(a[num],a[left]);
	while(left<right){
		while(left<right&&temp<a[right])right--;
		a[left]=a[right];
		while(left<right&&a[left]<=temp)left++;
		a[right]=a[left];
	}
	a[left]=temp;
	return left;
}
void  randselect(int a[],int left,int right,int k){
	if(left==right)return ;//边界,找到第k大的数; 
	int p=partition(a,left,right);//基准值的下标; 
	int m=p-left+1;//基准值是数列里面的第m大的数; 
	if(m==k)return ;//如果刚好是基准值;
	if(k<m){
		randselect(a,left,p-1,k);//在左区间找; 
	}else {
		randselect(a,p+1,right,k-m);//在右区间的一个新数列找,新数列的开始是p+1,所以是k-m,即是新数列的第k-m大的数,而不是第k大的数; 
	}
}
int main(){
	srand((unsigned)time(NULL));
	int sum1=0;int sum2=0;int sum=0;
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>a[i];
		sum+=a[i];
	}
	randselect(a,0,n-1,n/2);
	for(int i=0;i<n/2;i++)sum1+=a[i];
	sum2=sum-sum1;
	cout<<sum2-sum1;
	return 0;
} 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值