在此前,已经介绍了向下调整算法,建堆以及堆排序的实现,这篇文章将实现TopK问题。
前提:从1000个数据中找出10个最小的,这里的实现用到了文件操作,现实中若是基数很大,不能直接在内存中保存,则要借助文件将数据保存,然后再提取数据进行比较。大概步骤如下:
1:生成1000个随机数,做为本次要排序的基数。
2:为了找10个最小的,即提取出10个数来建堆,建大堆(建大堆即这10个元素中最大的元素在堆顶,然后将剩下的元素依次提取,与堆顶比较,若是比堆顶小,则将堆顶替换为该数据,再进行调整,变为大堆,调整后堆顶数据还是新的10个数据中最大的,然后继续提取,比较,调整)。
3:遍历剩下的所有元素。
创建基础数据:
void CreatDate(char* filename, int N)
{
FILE* fin = fopen(filename, "w");
if (fin == NULL)
{
perror("CreatDate");
}
srand(time(NULL));
int i = 0;
while (i < N)
{
fprintf(fin,"%d\n",rand()%1000);
i++;
}
fclose(fin);
}
这里是创建N个数据,rand()%1000将数据范围限制在0-999,rand()使用前要先调用srand()这样才能使每次生成的数据有随机行,不然每次生成的数据顺序都是一样的。
建堆:
void Swap(int* p1, int* p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
//大堆,找最小
void AdjustDown(int* arr, int parent, int K)
{
int child = 2 * parent + 1;
while (child < K)
{
if (child + 1 < K && arr[child + 1] > arr[child])
{
child = child + 1;
}
if (arr[child] > arr[parent])
{
Swap(&arr[child], &arr[parent]);
parent = child;
child = 2 * parent + 1;
}
else
{
break;
}
}
}
TopK实现:
void PrintTopK(char* filename, int K)
{
FILE* fout = fopen(filename, "r");
if (fout == NULL)
{
perror("PrintTopK");
}
//读取前k个数,
int* arr = (int*)malloc(sizeof(int) * K);
if (arr == NULL)
{
exit(-1);
}
int i = 0;
for (i = 0; i < K; i++)
{
fscanf(fout, "%d\n", &arr[i]);
}
//建堆
for (i = (K-1-1)/2; i >= 0; i--)
{
AdjustDown(arr,i,K);
}
//遍历剩下的元素,与堆顶元素比较,比堆顶小则替换堆顶,
int num = 0;
while (fscanf(fout, "%d\n", &num) != EOF)
{
if (arr[0] > num)
{
arr[0] = num;
AdjustDown(arr, 0, K);
}
}
//打印最后的k个数据
for (i = 0; i < K; i++)
{
printf("%d ", arr[i]);
}
fclose(fout);
free(arr);
arr = NULL;
}
主函数:
int main()
{
char* filename = "Date.txt";
int N = 1000;
int K = 10;
//CreatDate(filename, N);
PrintTopK(filename, K);
return 0;
}