一、什么是 Top-K 问题?
Top-K 问题是一类常见的算法问题,其中目的是从一组元素中找到排名前K的元素。具体来说,对于给定的一组数据,Top-K 问题要求找到其中最大(或最小)的K个元素。
二、解决思路
以找出最大的5个数据为例,正常来说,可以建一个大堆,然后Pop5次,就能获得最大的5个数据。但是如果数据量非常大,比如10亿,20亿,100亿,那么建堆的消耗就太大了。
优解:
建一个有5个数据的小堆,剩下的数据分别与堆顶数据比较,如果比堆顶数据大,那就替换堆顶数据,然后进行向下调整。所有数据比较完成后,堆里的5个数据就是最大的5个。
找最大的前k个数,建小堆;找最小的前k个数,建大堆。
三、程序代码
先创建一个足够多整数的文件
void CreateFile()
{
int n = 100000;
srand((unsigned int)time(NULL));
const char* file = "data.txt";
FILE* f = fopen(file, "w");
if (f == NULL)
{
perror("fail");
return;
}
while (n--)
{
int x = rand() % 10000000;
fprintf(f, "%d\n", x);
}
fclose(f);
}
这里创建了包含100000个整数的文件。
建堆排序
void AdjustDown(int* a, int n, int parent)
{
int child = (parent * 2) + 1;
while (child < n)
{
if (child + 1 < n && a[child] > a[child + 1])
{
child++;
}
if (a[parent] > a[child])
{
Swap(&a[parent], &a[child]);
parent = child;
child = (parent * 2) + 1;
}
else
{
break;
}
}
}
void TopK()
{
int k = 0;
printf("请输入:");
scanf("%d", &k);
int* head = (int*)malloc(sizeof(int) * k);
if (head == NULL)
{
perror("fail");
return;
}
const char* file = "data.txt";
FILE* f = fopen(file, "r");
if (f == NULL)
{
perror("fail");
return;
}
for (int i = 0; i < k; i++)
{
fscanf(f, "%d", &head[i]);
}
//前k个最大的,小堆
for (int i = (k - 1 - 1) / 2; i >= 0; i--)
{
AdjustDown(head, k, i);
}
int x = 0;
while (fscanf_s(f, "%d", &x) > 0)
{
if (x > head[0])
{
head[0] = x;
AdjustDown(head, k, 0);
}
}
for (int i = 0; i < k; i++)
{
printf("%d ", head[i]);
}
fclose(f);
}