目录
0.前言
在我们的日常生活中总有排名系统,找出前第k个分数最高的人,而现在让我们用堆来在有限内存中进行实现
1.知识准备
想要实现topk问题首先我们要有堆排序的知识 如果想要了解的可以看我这一篇堆的实现与堆排序(纯C语言版)-CSDN博客
2.实现
首先我们很容易想到,方法1就是建一个N个数的大堆,然后Pop k次这样我们就得到了前k个大的数,但是这样有个缺陷,当数据量过大时,我们将会浪费大量的内存,如要读取10亿个整数的话,需要大约4G的内存
那我们可以在1KB的情况下完成这个吗,答案是可以的
我们可以先建立一个只有K个数的小堆,然后后面读取的数据与堆顶数据比较,满足要求则覆盖掉堆顶数据再向下调整,只需要这样我们就只用不到kb实现了大数据的topk问题
接下来让我们看代码的实现
1.首先是必要的HeapSort
void HeapSort(elementType* arr,int size)
{
//O(Nlogn)
//for (int i = 0; i < size; i++)
//{
// AdjustUp(arr, i);
//}
//O(n)
for (int i = (size - 1 - 1)/2; i >= 0; i--)
{
AdjustDown(arr, i, size);
}
//进行排序 排序实质进行交换
int end = size - 1;
while (end>0)
{
Swap(&arr[0], &arr[end]);
AdjustDown(arr, 0, end);
end--;
}
}
void AdjustDown(elementType* arr, int parent, int size)
{
int child=parent*2+1;
while (child< size)
{
//先比较左右孩子的大小 小堆举例
if (child + 1 < size && arr[child] > arr[child + 1])//
child++;
if (arr[parent] > arr[child])
{
Swap(&arr[parent], &arr[child]);
parent = child;
child = child * 2 + 1;
}
else
{
break;
}
}
}
2.造数据
void CreateNDate()
{
// 造数据
int n = 10000;
srand(time(0));
const char* file = "data.txt";
FILE* fin = fopen(file, "w");
if (fin == NULL)
{
perror("fopen error");
return;
}
for (size_t i = 0; i < n; ++i)
{
int x = rand() % 1000000;
fprintf(fin, "%d\n", x);//1.注意在后面加上换行符向2.向文件中打印每个1000000以内的随机数
}
fclose(fin);
}
其他注意事项
当我们检测我们的程序是否正确的时候或者当我们发现我们程序出现了错误的时候,我们可以减少数据量,控制数据范围,手动添加特殊数据,就以我们的上面这个举例,当我们光看我们是否选择正确是相当困难的但是可以进行数据范围的控制可以
将int x = rand() % 1000000;
改为int x = rand() % 100;
这样数据范围就变为了0~99然后我们再去手动在我们生成的数据中添加特殊数据,这样我们就很容易就知道我们的程序是否正确
3.TopK的实现
时间复杂度为即深度(调整次数)乘以个数
void test03()//Topk解决方法2 只用k个整形数组的数据
{
FILE* file = fopen("data.txt", "r");
//先读取10个数据来进行堆的建立
int k = 0;
printf("请输入你要查找的前几大数据");
scanf("%d", &k);
int* arr = (int*)malloc(sizeof(int) * k);
for (int i = 0; i < 10; i++)
{
fscanf(file, "%d", arr+i);
}
//先建立一个能够存储10个数据的堆
for (int i = (k-1-1)/2; i>=0; i--)
{
AdjustDown(arr, i, k);
}
//然后开始读取后n-k个
int x = 0;
while (~fscanf(file,"%d",&x))
{
if (x > arr[0])
{
arr[0] = x;
AdjustDown(arr, 0, k);
}
}
printf("前k个最大数为->");
HeapSort(arr, k);
for (int i = 0; i < k; i++)
{
printf("%d ", arr[i]);
}
fclose(file);
free(arr);
}
学习笔记fscanf中第一个参数要写要读的文件名,第二个要写读取的格式,第三个是接受的元素然后啊fscanf的返回值与为什么要加上~这个取反符号,fscanf是有返回值的当读取成功返回0,当读取失败返回-1取反为0
其他注意事项,记得关闭文件与释放内存