堆的Top-K问题

⭐️ 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 nk 个元素依次与堆顶元素来比较,若比堆顶元素还大,则入堆向下调整,由于是小堆,所以大的数据都在堆底,所以最后堆里的数据就是前 k k k 个最大的元素。
那么找最大元素建大堆有什么问题?如果建的是大堆,堆顶假设已经是前 k k k 个最大的元素,那么依次与堆顶比较,第二大的元素根本进不了堆。反之同理。

代码:

#include 
#include 
#include 
#include 

// 向下调整算法
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();
	
}

堆的Top-K问题_第1张图片


你可能感兴趣的:(数据结构和算法,算法,学习,Top-k)