标签(空格分隔): 算法 面试
摘自:http://www.cnblogs.com/xudong-bupt/archive/2013/03/20/2971262.html
Q:有N(N>>10000)个整数,求出其中的前K个最大的数。(称作Top k或者Top 10)
A:由于(1)数据庞大;(2)只要前K个,对整个输入数据的保存和排序是相当的不可取的。
最小堆如图所示,对于每个非叶子节点的数值,一定不大于孩子节点的数值。这样可用含有K个节点的最小堆来保存K个目前的最大值(当然根节点是其中的最小数值)。
每次有数据输入的时候可以先与根节点比较。若不大于根节点,则舍弃;否则用新数值替换根节点数值。并进行最小堆的调整。
#include <stdio.h>
int n; ///数字个数,n很大(n>10000)
int dui[10];
#define K 10 ///Top K,K的取值
void create_dui(); ///建堆
void UpToDown(int); ///从上到下调整
int main()
{
int i;
int tmp;
while(scanf("%d",&n)!=EOF)
{
for(i=1;i<=K;i++)//先输入K个
scanf("%d",&dui[i]);
creare_dui();//建立小顶堆
for(i=K+1;i<=n;i++)
{
scanf("%d",&tmp);
if(tmp>dui[1]) //只有大于根节点才处理
{
dui[1] = tmp;
UpToDown(1);//向下调整堆
}
}
}
}
void create_dui()
{
int i;
int pos = K/2; //从末尾数,第一个非叶结点的位置为k/2
for(int i=pos;i>=1;i--)
UpToDown(i);
}
void UpToDown(int i)
{
int t1,t2,tmp,pos;
t1 = 2*i; // 左孩子(存在的话)
t2 = t1+1; // 右孩子(存在的话)
if(t1 > K) // 无孩子节点
return;
else
{
if(t2 > K) //只有左孩子
pos = t1;
else
pos = dui[t1] > dui[t2] ? t2 : t1;
if(dui[i] > dui[pos]) //pos保存在子孩子中,数值较小者的位置
{
tmp = dui[i];
dui[i] = dui[pos];
dui[pos] = tmp;
UpToDown(pos);
}
}
}
由于保存了 K 个数据,且调整最小堆的时间复杂度为O(lnK),所以TopK问题的时间复杂度为O(nlnK)。