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个

相关推荐
就叫飞六吧33 分钟前
Electron和C/C++开发桌面应用对比
c语言·开发语言·c++·visual studio
凡尘若诗1 小时前
数据结构关于链表的实践任务
数据结构·链表
weisian1511 小时前
Redis篇-11--数据结构篇3--字符串内存模型(简单动态字符串SDS)
数据结构·数据库·redis
7yewh2 小时前
LeetCode 力扣 热题 100道(二十)三数之和(C++)
c语言·数据结构·c++·算法·leetcode
代码雕刻家2 小时前
2.12.顺序表和链表的比较
数据结构·链表·顺序表
FranYeCisco3 小时前
C++基础:操作符
数据结构·c++·算法
a0023450014 小时前
冒泡排序与快速排序以及用快速排序的思想改进我们的冒泡排序
c语言
集大周杰伦4 小时前
C语言指针与数组深入剖析及优化示例 指针解读 数组与指针的关系
c语言·c
Jambo!5 小时前
Visual studio中C/C++连接mysql
c语言·c++·mysql·visual studio
旷野..5 小时前
谁说C比C++快?
java·c语言·c++