浅谈topk问题

topk问题

从N个数中找出最大(最小)的前k个
现实中的场景如:王者荣耀英雄排名(长安区第一百韩信),外面商家店铺排名(长安区冒菜销量第二)

1.思路一:排序(堆排序)

时间复杂度为:O(N*log2N)

如我们要找出最大的前k个数,我们可以对这N个数进行一个降序排序,然后输出前k个数即可;降序我们就要建小堆,然后将根节点和最后一个叶子节点交换,不把最后一个数看作堆里的数据,然后在进行向下调整;具体分析参考堆排序

1.1 代码实现

//降序
void HeapSort(int* a, int n)
{
    
    
	//建小堆
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
    
    
		AdjustDown(a, n, i);
	}
	int end = n - 1;
	//把最小的换到最后一个位置,不把最后一个数看作堆里的
	//每次选出剩下数中最小的
	//从后往前放
	while (end > 0)
	{
    
    
		int tem = a[end];
		a[end] = a[0];
		a[0] = tem;
		AdjustDown(a, end, 0);
		--end;
	}
}

1.2测试

int main()
{
    
    
	int arr[] = {
    
     27, 28, 65, 25, 15, 34, 19, 49, 18, 37 };
	int n = sizeof(arr) / sizeof(int);
	printf("全部数据:");
	for (int i = 0; i < n; i++)
	{
    
    
		printf("%d ", arr[i]);
	}
	printf("\n");
	HeapSort(arr, n);
	printf("最大的前5个数:");
	for (int j = 0;j < 5; j++)
	{
    
    
		printf("%d ", arr[j]);
	}
	printf("\n");
	return 0;
}

在这里插入图片描述

2.思路二:建大堆,,删掉最大的,调堆继续选,直到选出k个次大的

时间复杂度为:O(N+k*log2N)
当N非常大时此思路的时间复杂度趋近于O(N)

由于思路一效率不高,优化后得到思路二------->如果我们要找出最大的前k个数,我们可以建一个大堆,然后将根节点(最大值)删除,然后使用向下调整算法选出次大的,再删除,直到选出k个次大的数

此思路需要建一次堆时间复杂度为O(N),进行k次向下调整算法时间复杂度为O(k*log2N),所以此思路的总时间复杂度为O(N+klog2N),当N非常大时log2N非常平缓,而O(N)趋近于无穷大,所以当N非常大时此思路的时间复杂度趋近于O(N)

3.思路三–>最优解(省空间,效率相对较高)

时间复杂度为:O(k+(N-k)*log2k)
当N非常大时此思路的时间复杂度趋近于O(N)

若N非常大内存中放不下N个数思路二就不可行,优化得到思路三------->如果我们要找出最大的前k个数,建K个数大小的小堆,剩下的N-K个数依次去跟堆顶的数据比较,如果比堆顶的数据大,就替换(直接覆盖)堆顶的数据,再向下调整,如此循环最后最大的前K个数就在堆里

此思路建k个数大小的堆,时间复杂度为O(k),需要进行N-K次向下调整所以时间复杂度为O((N-k)*log2k),所以思路三总时间复杂度为O(k+(N-k)*log2k),实际应用中N是非常大的,而k相比于N就是一个非常小的值,所以当N非常大时思路三的时间复杂度也趋近于O(N)

3.1代码实现

void TopK(int* a,int k,int n)
{
    
    
	//开辟k个大小的空间
	int* heapA = (int*)malloc(k * sizeof(int));
	for (int i = 0; i < k; ++i)
	{
    
    
		heapA[i] = a[i];
	}
	//建k个数大小的小堆
	for (int i = (k - 2) / 2; i >= 0; --i)
	{
    
    
		AdjustDown(heapA, k, i);
	}
	//从第K个位置开始和heapA[0]比较,大就覆盖heapA[0],
	//然后继续调整,heapA[0]始终是堆里最小的数
	for (int i = k; i < n; i++)
	{
    
    
		if (a[i] > heapA[0])
		{
    
    
			heapA[0] = a[i];
			AdjustDown(heapA, k, 0);
		}
	}
	//打印
	for (int i = 0; i < k; i++)
	{
    
    
		printf("%d ", heapA[i]);
	}
	printf("\n");
	free(heapA);
}

3.2测试

int main()
{
    
    
	int arr[] = {
    
     27, 28, 65, 25, 15, 34, 19, 49, 18, 37 };
	int n = sizeof(arr) / sizeof(int);
	printf("全部数据:");
	for (int i = 0; i < n; i++)
	{
    
    
		printf("%d ", arr[i]);
	}
	printf("\n");
	TopK(arr, 5, n);
	return 0;
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_50886514/article/details/114928789
今日推荐