数据结构—Top K问题

在海量数据中,如何找出出现频率最高的前k个数或最大的前k个数?本文介绍了一种基于堆数据结构的解决方案,详细阐述了算法思想,并提供了具体的实现代码,展示了在有限空间下解决此类问题的方法,最后分析了时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

提示:这里解决 top K 问题是基于堆数据结构下的,对这块还有问题的可以点以下链接:

https://blog.youkuaiyun.com/z_x_m_m_q/article/details/82320357

Top K问题:

Topk问题是一个经典的海量数据处理问题, 在海量数据中找出出现频率最高的前k个数,或者从海量数据中找出最大的前k个数,这类问题通常被称为Top K问题

比如,热搜上每天更新出的排行前10的热门搜索信息,陕西人最爱吃的水果种类等,都可以使用topK问题来解决

海量数据:

海量数据拥有的一大特点就是数据量比较大, 一般情况下内存空间是不足以一次性来存储的,或者处理海量数据是往往有空间上的限制

例如:面试官给你100W个数据,请找出其中最大的前K个数,并且现在仅仅有1M的空间?

在32位操作系统中,默认每个数据为4字节,很显然 1M 的空间是存放不了要处理的目标数据

算法思想:

从大量数据中找出前 K 个最大数为例来说明:

首先以给定数据中的前 K 个数建立小堆,然后从 K+1 个数据开始与堆顶元素作比较,小于或等于堆顶元素时,保持建立的小堆不变;大于堆顶元素时,将该数据与堆顶元素交换,此时有可能破坏了小堆的有序性,需要从堆顶元素开始来一次向下调整使堆恢复成小堆

算法代码:

 这里我提供一个 main 函数开始的完整的代码,还有部分注释,供大家参考

#include"pch.h"
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
#include<time.h>

void Swap(int* x,int* y)  //交换函数,建议使用这种方式交换
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

void HeapAdjustDown(int* a,int n,int root)  //堆的向下调整函数
{
	int parent = root;
	int child = 2 * parent + 1;

	while (child < n) {
		if (child+1 < n && a[child+1] < a[child])
			++child;

		if (a[child] < a[parent])
			Swap(&a[child], &a[parent]);
		else
			break;

		parent = child;
		child = 2 * parent + 1;
	}
}

void Top_K(int* a,int n,int k)
{
	int i = 0;

	int* hp = (int*)malloc(sizeof(int)*k);  //用于始终存储堆中的 K 个数,记得释放空间
	for (i = 0;i < k;++i)
		hp[i] = a[i];

	for (i = (k-2)/2;i >= 0;--i)  //建堆
	{
		HeapAdjustDown(hp,k,i);
	}

	for (i = k;i < n;++i)  //从 K+1 个数开始与堆顶元素一一比较
	{
		if (a[i] > hp[0])  //a[0]:堆顶元素
		{
			hp[0] = a[i];
			HeapAdjustDown(hp, k, 0);
		}
	}

	for (i = 0;i < k;++i)  //简单的看下输出结果
	{
		printf("%d ",hp[i]);
	}

	free(hp);
	hp = NULL;
}

int main()
{
	int i = 0;
	int a[1024];

	srand((unsigned)time(NULL));

	for (i = 0;i < 1024;++i)
	{
		a[i] = rand();
	}

	Top_K(a,sizeof(a)/sizeof(a[0]),8);

	return 0;
}

结果展示: 

 

上面从大量数据中找出的最大的 8 个数显然满足小堆的性质。 

时间复杂度: 

基于堆数据结构下的 Top K 问题时间复杂度为:O(N\lg K),这是很显然的 

以下是一个使用Python实现的维护TopK元素的数据结构,基于最小堆(min-heap)来实现。这种数据结构非常适合处理动态数据流中的TopK问题。 ```python import heapq class TopK: def __init__(self, k): self.k = k self.min_heap = [] def add(self, num): # 如果堆中元素少于k个,则直接加入 if len(self.min_heap) < self.k: heapq.heappush(self.min_heap, num) else: # 如果当前数字比堆顶元素大,则替换堆顶元素 if num > self.min_heap[0]: heapq.heapreplace(self.min_heap, num) def get_top_k(self): # 返回堆中的所有元素(即TopK) return self.min_heap # 示例用法 topk = TopK(3) data_stream = [5, 1, 3, 6, 8, 2, 4] for num in data_stream: topk.add(num) print("TopK elements:", topk.get_top_k()) ``` ### 上述代码解释: 1. **`__init__` 方法**:初始化一个空的最小堆,并设置需要维护的TopK大小 `k`。 2. **`add` 方法**:向数据结构中添加一个新元素。如果堆中元素数量小于 `k`,则直接将该元素加入堆;否则,只有当新元素大于堆顶元素时,才将其替换进堆。 - 使用了 `heapq` 模块提供的 `heappush` `heapreplace` 函数来操作堆。 3. **`get_top_k` 方法**:返回当前堆中的所有元素,这些元素就是当前的TopK。 --- ### 数据结构选择的原因: - **最小堆** 是维护TopK问题的最佳选择之一。通过保持一个大小为 `k` 的最小堆,我们可以快速地在每次插入时判断是否需要更新TopK集合。 - 时间复杂度分析: - 插入单个元素的时间复杂度为 \(O(\log k)\),因为堆的操作(插入或替换)的时间复杂度是 \(O(\log k)\)。 - 获取TopK元素的时间复杂度为 \(O(1)\),因为我们直接返回堆中的元素。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值