堆的应用(讲解超详细)

1. 堆排序

说起排序,我们并不陌生,前面的冒泡排序就是其中之一,现在介绍一种新的排序方法 -- 堆排序。

思路分析:

第1步:建立有效的堆(什么叫有效的堆?-- 就是上节我们说的大根堆/小根堆);

第2步:进行升序和降序操作。

cpp 复制代码
void swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

//向下调整算法
void AdjustDown(int* arr, int parent, int n)
{
	//注意前面调整为小堆,这里删除也是调整小堆--要保持唯一
	int child = 2 * parent + 1;
	while (child < n)
	{
		//调整为大堆
		if (arr[child] < arr[child + 1] && child + 1 < n)
		{
			child++;//找大孩子
		}
		if (arr[child] > arr[parent])
		{
			swap(&arr[child], &arr[parent]);
			parent = child;
			child = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}
}

//堆排序
void Heapsort(int* arr, int n)
{
	
	//int parent = (n - 1 - 1) / 2;
	//找每一棵子树
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		//进行有效堆调整
		AdjustDown(arr, i, n);
	}
    //如果进行向上调整算法;每插入一个数据就要进行堆调整
    //for(int i = 0; i < n; i++)
    //{
        //AdjustUp(arr, i); // 具体使用方法见上节
    //}
	int end = n - 1;
	while (end > 0)
	{
		//堆顶和最后一个孩子交换
		swap(&arr[0], &arr[end]);
		//注意:交换后,孩子--
		end--;
		AdjustDown(arr, 0, end);
	}
}

2. Top-K问题

说白了就是让我们找出前K个最大值/最小值。

前面我们刚了解了堆排序可以得到最大/小值,那么这里我们能不能用堆排序来解决问题呢?

答案当然是可以的。

我们要找到前K个最大值,那是不是跟1个值比较,只要比它大,我就存放数据。

这里存放数据我们又得需要一个数组了。

既然如此,我们通过建堆来试试:

如果建大堆,只要比堆顶小,与堆顶交换数据 -- 得到前K个最小值;

如果建小堆,只要比堆顶大,与堆顶交换数据 -- 得到前K个最大值;

再将余下的N-K个数据和堆顶比较,满足要求和堆顶交换数据

这里就有人疑惑了,为啥我不建大堆和余下数据比较,这不也能找到前K个最大值吗?

NONONO!!这时你犯了错误!!

注意这里我们要得到前K个最大值,堆顶的值如果小于m,此时堆顶和m发生交换,这时你原来堆顶的最大值就相当于被删了!!

那你打印的时候就会发现,数组只有第一个是最大/小值!!!!!千万要注意!!

cpp 复制代码
void swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

//向下调整算法
void AdjustDown(int* arr, int parent, int n)
{
	int child = 2 * parent + 1;
	while (child < n)
	{
		//调整为小堆
		if (arr[child] > arr[child + 1] && child + 1 < n)
		{
			child++;//找大孩子
		}
		if (arr[child] < arr[parent])
		{
			swap(&arr[child], &arr[parent]);
			parent = child;
			child = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}
}

//要找出前K个最大值的前提是得有数据
void CreatData()
{
	//造数据
	int n = 10000;
	srand(time(0));
	const char* file = "data.txt";//创建一个文件
	FILE* fin = fopen(file, "w");//打开文件,写模式
	if (fin == NULL
		)
	{
		perror("fopen fail!");
		exit(1);
	}
	for (int i = 0; i < n; i++)
	{
		int x = (rand() + i) % 10000;
		fprintf(fin, "%d\n", x);//这里是把数据输入到文件file里
	}
	fclose(fin);
}
//找TOP-K问题
void Find()
{
	//找前K个数据
	int k = 0;
	printf("请输入k:");
	scanf("%d", &k);

	//打开文件读取数据
	const char* file = "data.txt";
	FILE* fout = fopen(file, "r");
	if (fout == NULL)
	{
		perror("fopen fail!");
		exit(1);
	}
	int* tmp = (int*)malloc(sizeof(int) * k);
	if (tmp == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}
	//读取前K个数据
	for (int i = 0; i < k; i++)
	{
		fscanf(fout, "%d", &tmp[i]);
	}
	//读完数据开始键堆并调整
	for (int i = 0; i < k; i++)
	{
		AdjustDown(tmp, 0, k);
	}
	int num = 0;
	//遍历余下的数据进行比较
	while (fscanf(fout, "%d", &num) != EOF)
	{
		//和堆顶进行比较
		if (num > tmp[0])
		{
			tmp[0] = num;
			//堆调整
			AdjustDown(tmp, 0, k);
		}
	}
	//打印tmp
	for (int i = 0; i < k; i++)
	{
		printf("%d  ", tmp[i]);
	}
	//注意文件结束操作后,要关闭文件!!
	fclose(fout);
}
相关推荐
jeffery8926 小时前
4056:【GESP2403八级】接竹竿
数据结构·c++·算法
一枝小雨8 小时前
【数据结构】排序算法全解析
数据结构·算法·排序算法
略知java的景初8 小时前
深入解析十大经典排序算法原理与实现
数据结构·算法·排序算法
liweiweili1269 小时前
main栈帧和func栈帧的关系
数据结构·算法
竹杖芒鞋轻胜马,夏天喜欢吃西瓜10 小时前
二叉树学习笔记
数据结构·笔记·学习
_OP_CHEN10 小时前
数据结构(C语言篇):(二)顺序表
c语言·数据结构·学习笔记·入门·顺序表·动态顺序表·静态顺序表
爱编程的鱼11 小时前
C# 数组&C# 多维数组
数据结构·算法·c#
上海迪士尼3512 小时前
除自身以外数组的乘积是什么意思
数据结构·算法
野犬寒鸦14 小时前
力扣hot100:字母异位词分组和最长连续序列(49,128)
java·数据结构·后端·算法·哈希算法
j_xxx404_14 小时前
数据结构:单链表的应用(力扣算法题)第一章
c语言·数据结构·算法·leetcode