随机选择算法

如何从一个无序数组中求出第K大的数?时间复杂度要达到O(n)
对A[left,right]进行一次randPartition函数后,主元素左侧的元素个数就是确定的,且他们都小于主元素。假设此时主元为A[p],那么A[p]就是A[left,right]中的第p-left+1大的数。不妨令M表示p-left+1,那么如果KM成立,说明第K大的数就是主元A[p],如果K<M则说明第K大的数在主元左侧,即A[0,1,…,p-1]中的第K大,往左递归即可;如果K>M,则说明第K大的数在主元右侧,即A[(p+1),…,right]中的第K-M大,往右侧递归即可。算法leftright作为递归边界。
给定一个整数集合,集合元素中的整数各不相同,将它分为两个子集合,使得两个子集合并起来为原集合,交起来空,同时在两个元素个数n1-n2之差尽可能小的情况下,要求它们各自的元素之和差的绝对值|S1-S2|尽可能大。

#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 100010;
int A[maxn],n;
//选取主元,对区间[left,right]进行划分
int randPartition(int A[],int left,int right)
{
	//生成[left,right]内随机数p
	int p = round(1.0*rand()/RAND_MAX*(right - left) + left);
	swap(A[p],A[left]);//交换A[left]、A[p]
	int temp = A[left];
	while(left<right)
	{
		while(left<right&&A[right]>temp)right--;//反复左移right
		A[left] = A[right];
		while(left<right&&A[left]<=temp)left++;//反复右移
		A[right] = A[left];
	}//while
	A[left] = temp;
	return left;
}//end of randPartition
//随机选择算法,从A[left,right]中返回第K大的数
void randSelect(int A[],int left,int right,int K)
{
	if(left==right)return ;//边界
	int p = randPartition(A,left,right);//划分后主元位置为p
	int M = p - left + 1;
	if(K == M) return;//找到第K大的数
	if(K<=M)//最大元在左侧
	{
		randSelect(A,left,p - 1,K);//在主元左侧寻找
	}//<=
	else
	{
		randSelect(A,p + 1,right,K - M);
	}
}//end of randSelect 
int main()
{
	srand((unsigned)time(NULL));//初始化随机种子
	//sum 和 sum1记录所有整数之和与切分后前n/2个元素的和
	int sum = 0,sum1 = 0;
	cin>>n;//number of int
	for(int i=0;i<n;i++)
	{
		cin>>A[i];
		sum+=A[i];
	}
	randSelsect(A,0,n-1,n / 2);//寻找第n/2大的数,并进行切分
	for(int i = 0;i < n/2;i++)
	{
		sum1 += A[i];//累计较小子集的元素和
	}
	cout<<sum-sum1-sum1<<endl;
	
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值