小码农教你Topk

「这是我参与11月更文挑战的第20天,活动详情查看:2021最后一次更文挑战

Topk

在n个数中找出最大的前K个 or 在n个数中找出最小的前K个

(n>K)

1000个数中找到最大的前十个

方式1:

先排降序,前十个就是最大的。时间复杂度O(N*log2N)

方式2:

N个数依次插入大堆,PopK次,每次取堆顶的数据就是最大的前K个。时间复杂度O(N+log2N)(下篇证明)

方式3:

假设N非常大,N是10亿,内存中存不下这些数,他们存在文件中,k是100。那么方式1与方式2就都不可以用了。时间复杂度 O(K+(N-K)log2K)

10亿整数我们看看大概消耗多少空间

image-20211109004544437

1.用前K个数建立一个K个数的小堆

2.用剩下的N-K个数,依次跟堆顶的数据进行比较,如果比堆顶的数据大,就去替换堆顶的数据,在向下调整

3.最后堆里面K个数就是最大的K个数

image-20211109011437750

Topk打印函数TopkPrint

image-20211109095630047

void TopkPrint(int* a, int n, int k)
{
	//创建一个堆
	HP hp;
	//堆初始化
	HeapInit(&hp);
	//用前K个数建立一个K个数的小堆
	int i = 0;
	for (i = 0; i < k; i++)
	{
		HeapPush(&hp,a[i]);
	}
	//用剩下的N-K个数,依次跟堆顶的数据进行比较,如果比堆顶的数据大,就去替换堆顶的数据,在向下调整
	//替换就破坏结构了,我们用接口来实现,找到就先pop,然后push大的数就行
	for (i = k; i < n ; i++)
	{
		//堆顶元素小于数组中的数
		if (HeapTop(&hp) < a[i])
		{
			//先把堆顶的数给出掉
			HeapPop(&hp);
			//再插入这个数组中的数进堆 
			HeapPush(&hp, a[i]);
		}
	}
	//然后打印堆即可
	HeapPrint(&hp);
	HeapDestroy(&hp);
}
复制代码

没有修改的接口见 算法给小码农堆魂器--铁血柔情

改掉的接口

向上调整函数

//向上调整函数
void AdjustUp(HPDataType* a, int size, int child)
{
	assert(a);
	int parent = (child - 1) / 2;
#if HEAP
	while (child > 0)
	{
		if (a[parent] < a[child])//父亲小于孩子就交换(大堆)
		{
			Swap(&a[parent], &a[child]);
			//交换好后重新称呼一下孩子与父亲
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
#elif !HEAP
	while (child > 0)
	{
		if (a[parent] > a[child])//父亲大于孩子就交换(小堆)
		{
			Swap(&a[parent], &a[child]);
			//交换好后重新称呼一下孩子与父亲
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
#endif // HEAP
}

复制代码

向下调整函数

//向下调整函数
void AdjustDown(HPDataType* a, int size, int parent)
{
	assert(a);
	//创建一个孩子变量,有两个孩子就在这个上加1就行
	int child = parent * 2 + 1;
#if HEAP
	while (child < size)
	{
		//选大孩子
		if (child + 1 < size && a[child] < a[child + 1])
		{
			child++;
		}
		//大的孩子还大于父亲就交换
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
#elif !HEAP
	while (child < size)
	{
		//选小孩子
		if (child + 1 < size && a[child] > a[child + 1])
		{
			child++;
		}
		//小的孩子还小于父亲就交换
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
#endif // HEAP	
}
复制代码

然后在Heap.h文件中加入

//0 大堆模式  1小堆模式
#define HEAP   0       
复制代码

猜你喜欢

转载自juejin.im/post/7032452773539282974