TopK问题的三种解法

TopK问题是指从n个数据中取前K个数据,在生活中的应用也有很多,如游戏中xxx的排行榜前10名等。在这篇博客中我将主要利用去解决TopK问题。

堆排序

首先我们需要建一个堆,然后我们再进行堆排序,排好序后直接取前K个就可以了。需要注意的是在使用堆排序的时候,我们需要确定我们要排升序还是降序。如果是升序的话,我们要建一个大根堆;如果是降序的话,我们要建一个小根堆思路:(可以结合图来进行分析) 这里我们以排降序为例子进行说明,建好小根堆后,我们将堆顶元素与堆中的最后一个元素进行互换   (让最小的元素换到最后面,以此来达到降序),此时最后一个元素就是最小的那个元素了,然后让堆的大小 - 1,也就是我们将最后一个元素从堆中踢出,可以将它看做已排好序的一个集合。然后再对这个堆进行向下调整算法,重新让它成为一个小根堆,然后重复这段操作,直到堆中只剩1个元素。堆排序的时间复杂度为O(n*logn)。

//向下调整算法
void AdjustDown(int* a, int n, int root)
{
	int parent = root;
	int child = 2 * parent + 1;

	while (child < n)
	{
		//左孩子和右孩子进行比较,选出较小的【如果没有右孩子,不进行该比较】
		if (child + 1 < n && a[child] > a[child + 1])
		{
			child++;
		}
		//判断孩子节点和父节点【调成小堆】
		if (a[child] < a[parent])
		{
			int tmp = a[child];
			a[child] = a[parent];
			a[parent] = tmp;

			parent = child;
			child = 2 * child + 1;
		}
		else
		{
			break;
		}
	}
}

void HeapCreate(Heap* hp, HpDataType* a, int sz)
{
	//堆的空间开辟和初始化
	hp->_a = (HpDataType*)malloc(sizeof(HpDataType)*sz);
	hp->_size = sz;
	hp->_capacity = sz;
	//数据的拷贝
	memcpy(hp->_a, a, sizeof(HpDataType)*sz);

	//堆的创建
	int i = 0;
	for (i = (sz - 1 - 1) / 2;i >= 0;--i)
	{
		AdjustDown(hp->_a, sz, i);
	}
}

void PrintTopK(int* a, int n, int K)
{
	Heap hp;
	HeapCreate(&hp, a, K);
	HeapSort(&hp);

	int i = 0;
	for (i = 0; i < K; ++i)
	{
		printf("%d ", hp._a[i]);
	}
}

建一个大小为n的堆

我们利用所给的n个数据去建一个大小为n的堆,如果求的是最大的K个数据,那么我们需要建大根堆;要求最小的K个数据,我们要建小根堆。这里我们以求最大的K个数据为例子,我们建一个大小为n的大根堆,然后这个堆的堆顶元素(HeapTop),然后再对将堆顶元素进行删除(HeapPop)的操作,然后再进行向下调整算法。要求最大的K个,那么只要循环K次即可。

HpDataType HeapTop(Heap* hp)
{
	return hp->_a[0];
}

void HeapPop(Heap* hp)
{
	Swap(&hp->_a[0], &hp->_a[hp->_size - 1]);
	hp->_size--;
    //每进行完1次删除,都要进行1次向下调整
	AdjustDown(hp->_a, hp->_size, 0);
}

void PrintTopK(int* a, int n, int K)
{
	Heap hp;
	HeapCreate(&hp, a, K);

	int i = 0;
	for (i = 0; i < K; ++i)
	{
		printf("%d ", HeapTop(&hp));
		HeapPop(&hp);
	}
}

建一个大小为K的堆

这里我们以求最大的K个数据为例子。首先,我们先用一组数据中的前K个数据建一个小根堆,然后依次的用剩下的数据与小根堆的堆顶元素进行比较,如果该元素大于堆顶元素,那么将该元素替换为堆顶元素,随后再进行向下调整,使其保持小根堆,当所有元素都比较完后,小根堆中的K个元素就是这些数据中的最大的K个元素了。

void PrintTopK(int* a, int n, int K)
{
	Heap hp;
    //用前K个元素去建一个小根堆
	HeapCreate(&hp, a, K);

    //用剩下的元素与堆顶元素进行比较
	int i = 0;
	for (i = K; i < n; ++i)
	{
		if (a[i] > HeapTop(&hp))
		{
			hp._a[0] = a[i];//替换堆顶元素
 			AdjustDown(hp._a, K, 0);
		}
	}
	for (i = 0; i < K;++i)
	{
		printf("%d ", hp._a[i]);
	}
}

猜你喜欢

转载自blog.csdn.net/weixin_51696091/article/details/115005289