Top k问题是什么?
Top k问题即在一些数据中找到前K个最大(小)的数据
例:
世界500强企业,即是世界上综合实力最强的前500个企业
这就是一个Top K问题,此时K=500;
Top k问题的解决方法
不解堆和向上(下)调整算法的话,可以看我的这篇文章
详解(实现)堆的接口函数
-
使用所以要比较的数据的前K个建一个可容纳k个数据的堆(找前k个
最大
的建小
堆,找前K个最小
的建大
堆)(1) -
将之后所有要比较的数据与所建的堆的堆顶比较,满足条件的覆盖掉堆顶数据,再让从堆顶开始进行一次向下调整算法保持堆的数据结构
-
所有数据都比较完之后,那可容纳K个数据的堆中的K个数据即是前K个最大(小)的数据
为什么找前k个最大
的建小
堆,找前K个最小
的建大
堆呢?
因为如果找前K个最大的数据
使用所以要比较的数据的前K个建了小
堆之后,该小堆堆顶的那个数据就是那K个数据中最小的,将之后要比较的其他数据与堆顶数据比较
如果连这K个中
最小的都比不过那它就肯定不是最大的前K个数
如果大于堆顶数据,就代表它有成为前K个最大的数据的可能,将改变了堆顶数据的堆向下调整之后,堆顶的数据就由是堆中K个数据中最小的那个了
然后再让其他要比较的数据与新堆顶数据比较,循环上述过程,每次有大于堆顶的数据都只替换掉了堆中最小的数据,最后比较完了之后对中的K个数据自然就是最大的前K个数据了
Top k问题解决代码
交换函数
void Swap(int* p, int* q)
{
int tmp = *p;
*p = *q;
*q = tmp;
}
向下调整算法
void AdjustDown(int* a, int n, int root)
{
int parent = root;
int child = parent * 2 + 1;
while (child < n)
{
if (child + 1 < n && a[child + 1] < a[child])
{
child++;
}
if (a[child] < a[parent])
{
Swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
void PrintTopK(int k)
{
FILE* pf = fopen("sb.txt", "r"); 打开存储要比较的数据的文件
int i = 0;
int* a = (int*)malloc(sizeof(int) * k); 申请K个空间用于存放堆的数据
if (a == NULL)
{
printf("PrintTopK中malloc失败");
return;
}
for (i = 0; i < k; i++)
{
int x = 0;
fscanf(pf, "%d", &x); 从文件中读取前K个数据到顺序表中
a[i] = x;
}
找最大建小堆
i = 0;
for (i = (k - 1 - 1) / 2; i >= 0; i--) 使用向下调整算法建堆
AdjustDown(a, k, i);
i = k;
for (i = k; i < 10000; i++)
{
int z = 0;
fscanf(pf, "%d", &z); 从文件中读取K个数据之后的要比较的数据
if (a[0] < z) 如果读取出来的数据大于堆顶数据
{
a[0] = z; 用读取出来的数据覆盖堆顶数据
AdjustDown(a, k, 0); 从堆顶开始向下调整,保证数据更改之后也还是堆
}
}
i = 0;
for (i = 0; i < k; i++)
printf("%d ", a[i]); 打印前K个数据
fclose(pf); 关闭文件
}
文字注释版代码
void Swap(int* p, int* q)
{
int tmp = *p;
*p = *q;
*q = tmp;
}
void AdjustDown(int* a, int n, int root)
{
int parent = root;
int child = parent * 2 + 1;
while (child < n)
{
if (child + 1 < n && a[child + 1] < a[child])
{
child++;
}
if (a[child] < a[parent])
{
Swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
void PrintTopK(int k)
{
FILE* pf = fopen("sb.txt", "r");//打开存储要比较的数据的文件
int i = 0;
int* a = (int*)malloc(sizeof(int) * k);//申请K个空间用于存放堆的数据
if (a == NULL)
{
printf("PrintTopK中malloc失败");
return;
}
for (i = 0; i < k; i++)
{
int x = 0;
fscanf(pf, "%d", &x);//从文件中读取前K个数据到顺序表中
a[i] = x;
}
//找最大建小堆
i = 0;
for (i = (k - 1 - 1) / 2; i >= 0; i--)//使用向下调整算法建堆
AdjustDown(a, k, i);
i = k;
for (i = k; i < 10000; i++)
{
int z = 0;
fscanf(pf, "%d", &z);//从文件中读取K个数据之后的要比较的数据
if (a[0] < z)//如果读取出来的数据大于堆顶数据
{
a[0] = z;//用读取出来的数据覆盖堆顶数据
AdjustDown(a, k, 0);//从堆顶开始向下调整,保证数据更改之后也还是堆
}
}
i = 0;
for (i = 0; i < k; i++)
printf("%d ", a[i]);//打印前K个数据
fclose(pf);//关闭文件
}