【C语言数据结构】利用堆解决Topk问题

Topk问题:给定你一个数组a,内有n个元素,请问怎么可以找出他前k个最大/最小的数?

这个题我们可以利用堆的特性来解决。我们可以先创建一个大小为k的小堆(如果求前k个最小的数就创建大堆,这里以求前k个最大的数为例),并对其排序。此时排在堆顶的就是当前堆内最小的数。下一步我们将剩余n-k个数依次与堆顶元素比较,如果这个数比堆顶元素大,那么将堆顶元素删除并将这个数入堆,否则就检查下一个数。如此下来到最后,堆内剩下的元素就是所有数里最大的k个数了。

/*
这里是笔者实现的堆
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
typedef int HeapDataType;

typedef struct Heap
{
	HeapDataType* a;
	int size;
	int capacity;
}HP;
 void AdjustDown(int* a, int n, int parent)
{
	assert(a);
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child + 1 < n && a[child] > a[child + 1])
			child++;
		if (a[child] < a[parent])
		{
			int tmp = a[parent];
			a[parent] = a[child];
			a[child] = tmp;
			parent = child;
			child = parent * 2 + 1;
		}
		else
			break;
	}
}
void Adjustup(int* hp, HeapDataType child)
{
	assert(hp);
	int parents = (child - 1) / 2;
	while (child)
	{
		if (hp[child] < hp[parents])
		{
			HeapDataType tmp = hp[parents];
			hp[parents] = hp[child];
			hp[child] = tmp;

			child = parents;
			parents = (child - 1) / 2;
		}
		else
			break;
	}
}
void HeapInit(HP* hp)
{
	assert(hp);
	hp->capacity = hp->size = 0;
	hp->a = NULL;
}
void HeapDestroy(HP* hp)
{
	assert(hp);
	free(hp->a);
	hp->capacity = hp->size = 0;
}
void HeapPush(HP* hp, HeapDataType x)
{
	assert(hp);
	if (hp->capacity == hp->size)
	{
		hp->capacity = (hp->capacity == 0 ? 4 : hp->capacity * 2);
		HeapDataType* tmp = (HeapDataType*)realloc(hp->a, sizeof(HeapDataType) * hp->capacity);
		if (tmp == NULL)
			exit(-1);
		hp->a = tmp;
	}
	hp->a[hp->size] = x;
	hp->size++;
	Adjustup(hp->a, hp->size - 1);
}
void HeapPrint(HP* hp)
{
	assert(hp);
	for (int i = 0; i < hp->size; i++)
	{
		printf("%d ", hp->a[i]);
	}
	printf("\n");
}
void HeapPop(HP* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));
	hp->a[0] = hp->a[hp->size - 1];
	hp->size--;
	AdjustDown(hp->a, hp->size, 0);
}
int HeapSize(HP* hp)
{
	assert(hp);
	return hp->size;
}
bool HeapEmpty(HP* hp)
{
	assert(hp);
	return hp->size == 0;
}
HeapDataType HeadTop(HP* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));
	return hp->a[0];
}

*/
void PrintTopK(int* a, int n, int k)
{
	HP hp;
	HeapInit(&hp);
	for (int i = 0; i < k; i++)
	{
		HeapPush(&hp, a[i]);
		//先将前k个元素入堆,当全部入完后已经自动将最小的元素列在堆顶
	}
	//此时取后面的n-k个元素依次与堆顶元素比较
	for (int i = k; i < n; i++)
	{
		/*如果这个元素比堆顶元素大,那么直接删除堆顶元素并且将这个数入
		堆,否则就跳到下一个元素*/
		if (a[i] > hp.a[0])
		{
			HeapPop(&hp);
			HeapPush(&hp, a[i]);
		}
	}
	HeapPrint(&hp);
	//最后打印一下堆内元素
	HeapDestroy(&hp);
}

当求前k个最小的数时,就把堆变为大堆,判断条件改为当元素比堆顶元素小的时候删除堆顶元素并将这个元素入堆即可。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值