如何从一个无序数组中求出第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;
}