TopK问题(用堆解决)


我们继续来延续我们上面的TopK问题,TopK问题一般是在解决有很多数的情况下,我们的k是个和小的值,然后我们是要找到最小或者最大的K个数,这类问题我们也称之为TopK问题,面对这种的问题,如果数字不是很大的情况下,我们就可以写一个循环,然后直接插入堆,进行向上调整就可以解决问题,但是现在这个数很大,我们如果插入数据的话消耗特别大,所以这里就只创建出来k个大小的存储进行建堆,这里我们把数据可以存在我们的文件当中,便于我们进行观察。

首先我们这里给出的问题是在这么多的数据当中找出最大的K个数字,第一个会困扰我们的就是我们应该建立大堆还是小堆呢,答案是小堆

我们先来分析为什么

首先我们如果是建大堆的话,如果第一次进堆的就是最大的数,那我们这里就会面临的是后面所以的数都进不了堆,我们是不是要比这个堆大才能进堆,这就好比我们的下水管道被堵住了,什么都进不去一样,所以这里是建立小堆,那我们这里还需要就是malloc出来一个k个大小的空间,先进行建堆,因为我们上面讲过建堆可以用向下调整的方式进行建堆,所以这里我们先完成这个步骤。

代码就是下面的这个。

// 建一个k个数小堆
	int* minheap = (int*)malloc(sizeof(int) * k);
	if (minheap == NULL)
	{
		perror("malloc error");
		return;
	}

	// 读取前k个,建小堆
	for (int i = 0; i < k; i++)
	{
		fscanf(fout, "%d", &minheap[i]);
		AdjustUp(minheap, i);
	}

然后我们给出的规定就是如果比这个堆顶的数字是要大的,我们就直接进行覆盖,堆顶的数就直接变成这个大的数,然后我们向下调整,等所有的数字都进行比较之后,我们也可以取出我们的所有数字了。

	while (fscanf(fout, "%d", &x) != EOF)
	{
		if (x > minheap[0])
		{
			minheap[0] = x;
			AdjustDown(minheap, k, 0);
		}
	}

那因为我们是写在文件当中的,所有我们这里还有一个知识点就是文件操作

我们之前学过文件的读写,也知道要对一个文件进行读写是要先打开我们的文件,如果是写的方式就是会创建,而且没一次都会先清空(如果是有这个文件的话,没有就进行创建)

那打开之后,我们得用fscanffprintf这两个函数进行,那大家可以查看文档,其实就是和scanfprintf有不太一样的。

TopK问题(用堆解决)_第1张图片

TopK问题(用堆解决)_第2张图片
看这个大家可以回忆起来,要是忘记了可以看之前的文章进行回顾,文章名字就是文件操作

那我们后面就是对文章的读写,然后进行操作,完整代码。

#define _CRT_SECURE_NO_WARNINGS 



#include
#include


void CreateNDate()
{
	// 造数据
	int n = 10000000;
	srand(time(0));
	const char* file = "data.txt";
	FILE* fin = fopen(file, "w");
	if (fin == NULL)
	{
		perror("fopen error");
		return;
	}

	for (int i = 0; i < n; ++i)
	{
		int x = (rand() + i) % 10000000;
		fprintf(fin, "%d\n", x);
	}

	fclose(fin);
}

void PrintTopK(const char* file, int k)
{
	FILE* fout = fopen(file, "r");
	if (fout == NULL)
	{
		perror("fopen error");
		return;
	}

	// 建一个k个数小堆
	int* minheap = (int*)malloc(sizeof(int) * k);
	if (minheap == NULL)
	{
		perror("malloc error");
		return;
	}

	// 读取前k个,建小堆
	for (int i = 0; i < k; i++)
	{
		fscanf(fout, "%d", &minheap[i]);
		AdjustUp(minheap, i);
	}

	int x = 0;
	while (fscanf(fout, "%d", &x) != EOF)
	{
		if (x > minheap[0])
		{
			minheap[0] = x;
			AdjustDown(minheap, k, 0);
		}
	}

	for (int i = 0; i < k; i++)
	{
		printf("%d ", minheap[i]);
	}
	printf("\n");

	free(minheap);
	fclose(fout);
}

int main()
{
	CreateNDate();
	PrintTopK("Data.txt", 5);

	return 0;
}

当然这里还是需要注意一些东西,比如我们怎么知道那几个数是最大的,我们这里产生的随机数就是和有意思了

TopK问题(用堆解决)_第3张图片
保证这些数字都是0-9999999,这里加i也有作用,主要是取决于我们的srand函数产生一定的随机数之后就只会产生相同的随机数了,所有这里的好处就是后面产生的随机数是加上i,因为i也是一直变的,我们只要在产生的随机数里改掉几个比1000000大的数就可以知道我们的代码是不是正确的额,可以直接用数组下标的方式,当然也可以在打开的文件里改,反正都可以来看,还有就是我们的调试窗口。

那我们今天的内容就到此结束了,我们下次再见。

TopK问题(用堆解决)_第4张图片

你可能感兴趣的:(数据结构,TopK问题,堆排序)