⭐️ TOP-K问题
TOP-K问题:即求数据结合中前 k k k 个最大的元素或者最小的元素,一般情况数据量都比较大。
比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。
如果数据量过大,排序的方式就不太可取了。
思路:使用数据中的前 k k k 个元素来建堆。
- 前 k k k 个最大的元素,建小堆
- 前 k k k 个最小的元素,建大堆
如果要找前
k
k
k 个最大元素,用剩余的
n
−
k
n - k
n−k 个元素依次与堆顶元素来比较,若比堆顶元素还大,则入堆向下调整,由于是小堆,所以大的数据都在堆底,所以最后堆里的数据就是前
k
k
k 个最大的元素。
那么找最大元素建大堆有什么问题?如果建的是大堆,堆顶假设已经是前
k
k
k 个最大的元素,那么依次与堆顶比较,第二大的元素根本进不了堆。反之同理。
代码:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>
// 向下调整算法
void AdjustDown(HeapDataType* data, int size, int parent) {
assert(data);
// 默认左孩子最小
int child = parent * 2 + 1;
while (child < size) {
// 判断左孩子和右孩子谁更小
// 若右孩子小则改变child 右孩子不能越界
if ( (child + 1 < size) && (data[child + 1] < data[child]) ) {
child++;
}
// 最小的孩子是否比父节点小
if (data[child] < data[parent]) {
// 交换
Swap(&data[child] , &data[parent]);
// 迭代
parent = child;
child = parent * 2 + 1;
}
else {
// 父节点 <= 最小的孩子节点
break;
}
}
}
void TopK(int* a, int n, int k)
{
assert(k > 0);
int* heap = (int*)malloc(sizeof(int) * k);
assert(heap);
for (int i = 0; i < k; i++) {
heap[i] = a[i];
}
// 建堆 用a中前k个元素建堆
for (int i = (k - 1 - 1) / 2; i >= 0; i--) {
AdjustDown(heap , k , i);
}
// 将剩余n-k个元素依次与堆顶元素交换,不满则则替换
for (int i = k; i < n; i++) {
if (a[i] > heap[0]) {
heap[0] = a[i];
AdjustDown(heap , k , 0);
}
}
// 打印
for (int i = 0; i < k; i++) {
printf("%d " , heap[i]);
}
printf("\n");
}
// 模拟数据
void TopKTest()
{
int n = 10000;
int* a = (int*)malloc(sizeof(int) * n);
assert(a != NULL);
srand((unsigned int)time(NULL));
for (int i = 0; i < n; ++i)
{
a[i] = rand() % 1000000;
}
a[5] = 1000000 + 1;
a[1231] = 1000000 + 2;
a[531] = 1000000 + 3;
a[5121] = 1000000 + 4;
a[115] = 1000000 + 5;
a[2335] = 1000000 + 6;
a[9999] = 1000000 + 7;
a[76] = 1000000 + 8;
a[423] = 1000000 + 9;
a[3144] = 1000000 + 10;
TopK(a , n, 10);
free(a);
a = NULL;
}
int main () {
TopKTest();
}