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个

相关推荐
秋风&萧瑟7 分钟前
【数据结构】顺序队列与链式队列
linux·数据结构·windows
时时三省10 分钟前
【时时三省】(C语言基础)文件的顺序读写
c语言
graceyun10 分钟前
C语言进阶习题【1】指针和数组(4)——指针笔试题3
android·java·c语言
快乐飒男6 小时前
面试题目1
c语言
小猿_007 小时前
C语言程序设计十大排序—插入排序
c语言·算法·排序算法
siy233311 小时前
[c语言日寄]结构体的使用及其拓展
c语言·开发语言·笔记·学习·算法
安和昂12 小时前
effective Objective—C 第三章笔记
java·c语言·笔记
四念处茫茫12 小时前
【C语言系列】深入理解指针(2)
c语言·开发语言·visual studio
LucianaiB12 小时前
C语言之图像文件的属性
c语言·开发语言·microsoft·c语言之图像文件的属性
sci_ei12313 小时前
高水平EI会议-第四届机器学习、云计算与智能挖掘国际会议
数据结构·人工智能·算法·机器学习·数据挖掘·机器人·云计算