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]);
}
}