用堆解决Top k问题

Top k问题是什么?

Top k问题即在一些数据中找到前K个最大(小)的数据

例:
世界500强企业,即是世界上综合实力最强的前500个企业
这就是一个Top K问题,此时K=500;


Top k问题的解决方法

不解堆和向上(下)调整算法的话,可以看我的这篇文章
详解(实现)堆的接口函数

  1. 使用所以要比较的数据的前K个建一个可容纳k个数据的堆(找前k个最大的建堆,找前K个最小的建(1)

  2. 将之后所有要比较的数据与所建的堆的堆顶比较,满足条件的覆盖掉堆顶数据,再让从堆顶开始进行一次向下调整算法保持堆的数据结构

  3. 所有数据都比较完之后,那可容纳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);//关闭文件
}

猜你喜欢

转载自blog.csdn.net/2301_80058734/article/details/137082180
今日推荐