C语言---数据结构---堆

要想了解堆结构,首先要知道什么是堆、堆是用来做什么的。

那么什么是堆呢?

如果有一个关键码的集合K,K中包含n个数据,将这些元素按照完全二叉树的顺序存储方式存储在一个一维数组中,并满足第i个数据小于等于第2*i+1个数据且第i个数据小于等于第2*i+2个数据(i为数组下标),则称其为小堆(大堆:第i个数据大于等于第2*i+1个数据且第i个数据大于等于第2*i+2个数据)。将根节点最大的堆叫做大根堆或最大堆,将根节点最小的堆叫做小根堆或最小堆。

堆的性质:

1、堆中某个节点的值总是不大于或不小于其父节点的值;

2、堆总是一颗完全二叉树。

如何实现堆:

既然堆是由数组来实现的那么我们来提供一组数据,默认这组数据是一颗完全二叉树。

cpp 复制代码
int array[] = {27,15,19,18,28,34,65,49,25,37};

接下来我们利用这组数据使用向下调整算法来创建堆。

复制代码
void HeapSort(int* arr, int sz)
{
	//首先使用前几个数据进行建堆
    //对于一个包含 sz 个元素的数组,其最后一个非叶子节点的下标计算方式为 (sz - 2) / 2
    //(叶子节点本身就是一个完整的堆,所以不需要再进行调整)
	for (int i = (sz - 2) / 2; i >= 0; i--)
	{
		//向下调整
		AdjustDown(arr, i, sz);
	}

	//使堆顶数据和最后的数据进行交换
	//并继续进行向下调整算法
	int end = sz - 1;
	while (end > 0)
	{
		Swap(&arr[0], &arr[end]);
		AdjustDown(arr, 0, end);
		end--;
	}
}

void AdjustDown(int* arr, int parent, int sz)
{
	//左子树
	int child = parent * 2 + 1;
	while (child < sz)
	{
		//先判断左子树和右子树的大小
		//建小堆取大值,建大堆取小值
		if (child + 1 < sz && arr[child] > arr[child + 1])
		{
			child++;
		}
		//判断子树和父节点谁大谁小
		if (arr[child] < arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}

上面的代码是以建大根堆的形式展现的,如果需要建小根堆则调整判定条件即可

调整1:将左右孩子节点中的选择改为让较大值和父节点比较

调整2:如果孩子节点大于父节点再进行交换

建堆分为两种方法,一种是向上调整算法,一种是向下调整算法,向上调整一般用于数据的插入,向下调整一般用与数据的删除!

上面的是向下调整算法,下面是向上调整算法

复制代码
void AdjustUp(int* arr, int child)
{
	int parent = (child - 1) / 2;
	//不需要等于,child只要走到根节点的位置,根节点没有父节点不需要交换
	while (child > 0)
	{
		//建大堆,>
		//建小堆,<
		if (arr[child] < arr[parent])
		{
			Swap(&arr[parent], &arr[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
堆的作用

1.可以用于排序算法中的一项

2.可以解决一些用于处理大量数据中的前几项(最大的几项或者最小的几项)比如TOP-K问题

TOP-K问题

即获取一段数据中的前K个最大值或者最小值

首先来创建一段数据

复制代码
void CreateNDate()
{
	// 造数据
	int n = 10000;
	srand(time(0));
	const char* file = "data.txt";
	FILE* fin = fopen(file, "w");
	if (fin == NULL)
	{
		perror("fopen error");
		return;
	}
	for (size_t i = 0; i < n; ++i)
	{
		int x = rand() % 1000000;
		fprintf(fin, "%d\n", x);
	}
	fclose(fin);
}

然后使用前K个数据建堆

复制代码
//使用前K个数据造堆
for (int i = (k - 1 - 1) / 2; i >= 0; i--)
{
	//找前K个最大的数据使用小堆
	AdjestDown(arr, 0, n - 1);
}

接下来遍历数据中的每一个值,将大于堆顶的数据进堆并进行排序

复制代码
	//定义一个变量用于接收文件中的值
	int x = 0;
	while (fscanf(fout, "%d", &x) != EOF)
	{
		//将读取到的数据跟堆顶的数据进行比较
		//读取到的数据大于堆顶的数据,进行交换
		if (x > arr[0])
		{
			arr[0] = x;
			AdjestDown(arr, 0, n);
		}
	}

此时我们就得到了所有数据中最大的前K个

相关推荐
BS_Li1 分钟前
顺序表和链表
数据结构·链表·顺序表
monstercl16 分钟前
【C语言】信号
c语言·开发语言
姜行运19 分钟前
数据结构入门【算法复杂度】
android·c语言·数据结构·算法
f狐0狸x35 分钟前
【蓝桥杯每日一题】3.25
开发语言·数据结构·c++·算法·蓝桥杯
么耶咩_5153 小时前
排序复习_代码纯享
数据结构·算法
大小胖虎6 小时前
数据结构——第六章:图
数据结构·笔记··最小生成树·拓扑排序·最短路径
珊瑚里的鱼10 小时前
第三讲 | C/C++内存管理完全手册
c语言·c++·笔记·程序人生·visualstudio·visual studio
越甲八千10 小时前
python 中match...case 和 C switch case区别
c语言·python·xcode
_GR11 小时前
2021年蓝桥杯第十二届C&C++大学B组真题及代码
c语言·数据结构·c++·算法·蓝桥杯
lwewan12 小时前
26考研——图_图的基本概念(6)
数据结构·考研