TopK问题

TopK问题:找出N个数里面最大/最小的前K个问题。

这里研究用堆解决

方法有三

1.堆排序  O(N*logN)  (或者其他排序)

2.建立N个大堆 ,每次取堆顶K次O(N+logN*K),

3.N远大于K的情况下:O(K+(N-K)*logK)

建立数量为k的一个小堆,剩下N-k与小堆堆顶比较,大的进堆,

如下图,绿圈为随机取k个

 不断把n-k个元素与小堆顶比较,大者交换。

最后就能保证小堆里都是前K个。

第三种方法与前两种方法相比,时间复杂度相差不大,优势在于空间复杂度。

代码

随机生成1000个数,并保证每个数字大小都在范围10000内。

随便取10个数令其超过一万,这10个数就是前k个数。

void TestTopk() {
	int n = 10000;
	int* arr = (int*)malloc(n * sizeof(int));
	for (int i = 0; i < 10000; i++)
	{
		arr[i] = rand() % 10000;
	}

	arr[32] = n + 1;
	arr[22] = n + 2;
	arr[24] = n + 3;
	arr[244] = n + 4;
	arr[212] = n + 5;
	arr[112] = n + 6;
	arr[2324] = n + 7;
	arr[2243] = n + 8;
	arr[2] = n + 9;
	arr[231] = n + 10;

	PrintTopK(arr, n, 10);
}

 实现找出前k个数的函数

void PrintTopK(int* a, int n, int k) {

	//建堆 O(K)  
	// 
	//k个元素建堆(数组)

	//抽k个数
	int* heap = (int*)malloc(k * sizeof(int));
	for (int i = 0; i < k; i++)
	{
		heap[i] = a[i];
	}
	//完成堆的结构构建:找最后一个非叶子结点下沉
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		HeapSink(heap, k, i);
	}


	//比较 O((K-N)logK)
	//遍历K-N个
	for (int i = k; i < n; i++)
	{
		if (a[i] > heap[0]) {
			//与堆顶交换后下沉 :logK
			swap(&a[i], &heap[0]);
			HeapSink(heap, k, 0);
		}
	}


	for (int i = 0; i < k; i++)
		printf("%d ", heap[i]);

}

heapSink的实现


void HeapSink(int* a, int size, int parent) {
	int child = parent * 2 + 1;
	while (child < size) {
		//选取2个孩子中较小的同时还得考虑,只有一个左子树的情况
		if (child + 1 < size && a[child] > a[child + 1])
		{
			child++;
		}
		if (a[child] < a[parent])
		{
			swap(&a[child], &a[parent]);
			parent = child;
			child = child * 2 + 1;
		}
		else {
			break;
		}
	}
}

 运行结果

10001 10002 10003 10005 10004 10008 10009 10006 10007 10010

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值