目录
一、介绍
TOP-K
问题:即求数据结合中前
K
个最大的元素或者最小的元素,一般情况下数据量都比较大。
比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。
二、思路
对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决,基本思路如下:
1.
用数据集合中前
K
个元素来建堆
前k个最大的元素,则建小堆
前k个最小的元素,则建大堆
2.
用剩余的
N-K
个元素依次与堆顶元素来比较,不满足则替换堆顶元素
将剩余N-K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素
三、代码实现
1.创造数据
当数据含量极大时,我们一般采用文件操作,去存储数据。
//创造数据
void CreatNDate()
{
int n = 1000;
srand((size_t)time(NULL));
const char* file = "date.txt";
FILE* fin = fopen(file, "w"); //文件指针
if (fin == NULL)
{
perror("fopen fail");
return;
}
for (int i = 0; i < n; i++)
{
int x = rand() % 100;
fprintf(fin, "%d\n", x); //对文件指针进行操作
}
fclose(fin); //关闭文件指针
}
2、排序
代码讲解:
代码具体可以分为以下步骤:
1.打开文件,读取数据
2.利用K个数据建堆
3.依次比较,进行替换、调整
4.释放空间、关闭文件、文件指针置空。
//TOP-K :先读取 再: 1.建堆 2.替换
//
//
// 求大,建小堆;求小,建大堆
void PrintTopK(int k)
{
const char* file = "date.txt"; //需要操作的文件名
FILE* fout = fopen(file, "r"); //文件指针会伴随操作,不断移动
if (fout == NULL)
{
perror("fopen fail");
return;
}
//读取
int* arr = (int*)malloc(sizeof(int) * k);
if (arr == NULL)
{
perror("malloc fail");
return;
}
for (int i = 0; i < k; i++)
{
fscanf(fout, "%d", &arr[i]); //空格、“\n”会被默认为换行符
}
//建堆:传最后一个父亲的下标建堆
for (int i = (k - 1 - 1) / 2; i >= 0; i--)
{
AdjustDown(arr, k, i);
}
//遍历筛选
//此时文件指针已经到了第6个数据
while (!feof(fout)) //当没有到文件末尾时。
{
int x = 0;
fscanf(fout, "%d", &x);
if (arr[0] < x)
{
arr[0] = x;
AdjustDown(arr, k, 0); //parent下标是 0
}
}
for (int i = 0; i < k; i++)
{
printf("%d ", arr[i]);
}
putchar(10);
free(arr);
arr = NULL;
}
fclose(fout);
fout = NULL;