题目链接:3:k大数
总时间限制: 10000ms 内存限制: 655360kB
求互异整数序列中第k大的数
输入
第一行 一个正整数n<5,000,000,表示序列长度
第二行 一个正整数0<=k后面n行每行一个整数。
序列中第k大的数(对于最小的数k=0)
【题目分析】
这是一个经典的求第K大的数的问题。首先看时间要求,看起来nlog(n)的时间复杂度的算法是可以的。所以首先考虑直接排序:
#include<stdio.h>
#include<stdlib.h>
int In[6000000];
int comp(const void * a, const void * b)
{
return *(int *) a - *(int *) b;
}
int main ()
{
int n,k;
scanf("%d%d", &n, &k);
for (int i = 0; i < n; i++)
scanf("%d",&In[i]);
qsort(In, n, sizeof(int), comp);
printf("%d\n",In[k]);
return 0;
}
发现就已经可以过了。但人得有点追求不是嘛~考虑到输入都在int的范围,范围比较小。于是设计了如下的最坏情况为n(1+logt-logn)的算法(t为输入数值的大小范围。)
#include<stdio.h>
int In[6000000];
int temp1[6000000], l1;
int temp2[6000000], l2;
int main ()
{
int n,k;
scanf("%d%d", &n, &k);
for (int i = 0; i < n; i++)
scanf("%d",&In[i]);
int * N = In, *t1 = temp1, *t2 = temp2;
int * tt;
int ln = n;
while(1)
{
int Max = 0x80000000;
int Min = 0x7fffffff;
for (int i = 0; i < ln; i++)
{
if (Max < N[i])
Max = N[i];
if (Min > N[i])
Min = N[i];
}
int Mid = (Max + Min) / 2;
if (k == 0)
{
printf("%d\n", Min);
break;
}
if (k == ln - 1)
{
printf("%d\n", Max);
break;
}
l1 = 0; l2 = 0;
for (int i = 0; i < ln; i++)
{
if (N[i] <= Mid)
{
t1[l1] = N[i];
l1++;
}
else
{
t2[l2] = N[i];
l2++;
}
}
if (k >= l1)
{
k -= l1;
tt = N; N = t2; t2 = tt;
ln = l2;
}
else
{
tt = N; N = t1; t1 = tt;
ln = l1;
}
}
return 0;
}
接下来又注意到这道题输入的数据范围在0~100000000之间,并且两两不重复。于是设计了如下基于桶排序的方法,最坏时间复杂度k+t(t为数据范围,k为输入的那个k)
#include<stdio.h>
bool B[100000000];
int main ()
{
int n,k;
scanf("%d%d", &n, &k);
for (int i = 0; i < n; i++)
{
int t;
scanf("%d",&t);
B[t] = 1;
}
int count = 0;
for (int i = 0;;i++)
{
if (B[i])
count++;
if (count == k + 1)
{
printf("%d",i);
break;
}
}
return 0;
}
最后,一时无聊,决定把这道题玩坏。于是把上面的桶排序的代码改到了140字节:
#include<stdio.h>
int n,k,t,B[99999999];main(){scanf("%d%d",&n,&k);while(n--)scanf("%d",&t),B[t]=1;for(k++;k;)k-=B[n],k?n++:printf("%d",n);}
需要注意的是,k表示的是这组数中从小数的第k个数。