【数据结构】堆(C语言)

今天我们来学习堆,它也是二叉树的一种(我滴神树!)

目录

堆的介绍:

如果有一个关键码的集合K = { , , ,..., },把它的所有元素按完全二叉树的顺序存储方式存储 在一个一维数组中,并满足: <= 且

<= ( >= 且 >= ) i = 0,1,

2...,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

堆的性质:

  • 堆中某个节点的值总是不大于或不小于其父节点的值;
  • 堆总是一棵完全二叉树。

堆的代码实现:

堆的结构体创建:

c 复制代码
typedef int HpDataType;

typedef struct Heap
{
	int size;
	int capacity;
	HpDataType* a;
}Hp;

堆的初始化:

这里我们选择先不给赋值,等push时再给赋值

c 复制代码
void HpInit(Hp* php)
{
	assert(php);

	php->a = NULL;
	php->size = 0;
	php->capacity = 0;
}

堆的销毁:

虽然与初始化相似,但是不能混用

c 复制代码
void HpDestory(Hp* php)
{
	assert(php);

	free(php->a);
	php->a = NULL;
	php->size = 0;
	php->capacity = 0;
}

堆的push:

我们需要一个向上调整算法:

这里我们选择创建小堆

因为我们只有push需要创建newnode,故不需要重新封装一个CreatNewnode函数调整算法时需要传的参数是

c 复制代码
void HpPush(Hp* php, HpDataType x)
{
	assert(php);

	if (php->capacity == php->size)
	{
		int newcapacity = (php->capacity == 0 ? 4 : php->capacity * 2);
		HpDataType* tmp = (HpDataType*)realloc(php->a, sizeof(HpDataType)* newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		php->a = tmp;
		php->capacity = newcapacity;
	}

	php->a[php->size] = x;
	php->size++;

	AdjustUp(php->a, php->size - 1);
}

向上调整算法:

注意:

我们在进行向上传参时,要传入动态数组的地址和最后一个叶子节点的下标,为什么不是传入结构体的地址原因会在后来讲解

c 复制代码
Swap(HpDataType* e1, HpDataType* e2)
{
	HpDataType tmp = *e1;
	*e1 = *e2;
	*e2 = tmp;
}

void AdjustUp(HpDataType* a, int child)
{
	int parent = (child - 1) / 2;
	//假设进入循环时child > 0
    //这里选择child = 0作为结束标志是因为当child = 0时
    //a[child] 与 a[parent]已经交换过一次了,
    //他们两现在同时指向下标位0,不需要在交换了
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
		}
		else
		{
			break;
		}

		child = (child - 1) / 2;
		parent = (parent - 1) / 2;
	}
}

堆的pop:

注意:

我们在进行pop时,并不是pop最后的叶子节点,这样没有实际意义,我们要pop的是根节点,这样是有实际意义的,比如Top k问题,堆排序

pop主体部分:

c 复制代码
void HpPop(Hp* php)
{
	assert(php);

	Swap(&php->a[php->size - 1], &php->a[0]);
	php->size--;

	AdjustDown(php->a, php->size, 0);
}

同理我们也需要一个向下调整算法

注意:

传参时仍然是传动态数组a的地址,另外还需要size与根节点0的下标,
size用于判断是否超出堆的范围,0作为parent的初始值

向下调整时我们需要找出孩子节点中较大或较小的那个,在这种情况下我们可以使用假设法,假设后在进行判断是否正确,将两段逻辑变成一段逻辑

c 复制代码
AdjustDown(HpDataType* a, int size, int parent)
{
	//假设法
	int child = parent * 2 + 1;

	while (child < size)
	{
		if (child + 1 < size && a[child] > a[child + 1])
		{
			child++;
		}

		if (a[parent] > a[child])
		{
			Swap(&a[parent], &a[child]);
		}
		else
		{
			break;
		}
		child = child * 2 + 1;
		parent = parent * 2 + 1;
	}
}

判空 && 求Top元素 && 求size:

c 复制代码
bool HpEmpty(Hp* php)
{
	assert(php);

	return php->size == 0;
}

int HpTop(Hp* php)
{
	assert(php);
	//注意为空
	assert(php->size);

	return php->a[0];
}

int HpSize(Hp* php)
{
	assert(php);

	return php->size;
}

完整源码:

heap.c

c 复制代码
#define _CRT_SECURE_NO_WARNINGS 1

#include"heap.h"

void HpInit(Hp* php)
{
	assert(php);

	php->a = NULL;
	php->size = 0;
	php->capacity = 0;
}

void HpDestory(Hp* php)
{
	assert(php);

	free(php->a);
	php->a = NULL;
	php->size = 0;
}

Swap(HpDataType* e1, HpDataType* e2)
{
	HpDataType tmp = *e1;
	*e1 = *e2;
	*e2 = tmp;
}

void AdjustUp(HpDataType* a, int child)
{
	int parent = (child - 1) / 2;

	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
		}
		else
		{
			break;
		}

		child = (child - 1) / 2;
		parent = (parent - 1) / 2;
	}
}
//小堆
void HpPush(Hp* php, HpDataType x)
{
	assert(php);

	if (php->capacity == php->size)
	{
		int newcapacity = (php->capacity == 0 ? 4 : php->capacity * 2);
		HpDataType* tmp = (HpDataType*)realloc(php->a, sizeof(HpDataType)* newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		php->a = tmp;
		php->capacity = newcapacity;
	}

	php->a[php->size] = x;
	php->size++;

	AdjustUp(php->a, php->size - 1);
}

AdjustDown(HpDataType* a, int size, int parent)
{
	//假设法
	int child = parent * 2 + 1;

	while (child < size)
	{
		if (child + 1 < size && a[child] > a[child + 1])
		{
			child++;
		}

		if (a[parent] > a[child])
		{
			Swap(&a[parent], &a[child]);
		}
		else
		{
			break;
		}
		child = child * 2 + 1;
		parent = parent * 2 + 1;
	}
}

void HpPop(Hp* php)
{
	assert(php);

	Swap(&php->a[php->size - 1], &php->a[0]);
	php->size--;

	AdjustDown(php->a, php->size, 0);
}

bool HpEmpty(Hp* php)
{
	assert(php);

	return php->size == 0;
}

int HpTop(Hp* php)
{
	assert(php);
	assert(php->size);

	return php->a[0];
}

int HpSize(Hp* php)
{
	assert(php);

	return php->size;
}

heap.h

c 复制代码
#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>

typedef int HpDataType;

typedef struct Heap
{
	int size;
	int capacity;
	HpDataType* a;
}Hp;

void HpInit(Hp* php);

void HpDestory(Hp* php);

void HpPush(Hp* php, HpDataType x);

void HpPop(Hp* php);

bool HpEmpty(Hp* php);

int HpSize(Hp* php);

int HpTop(Hp* php);

有疑问可以及时找博主交流

相关推荐
('-')30 分钟前
八股复习2:Java Array list和Linked list
java·开发语言
小黄人软件38 分钟前
C++读写编辑CSV文件示例源码 用于数据导入导出,比Excel好使
开发语言·c++·excel
郭涤生1 小时前
C++各个版本的性能和安全性总结
开发语言·c++
hoiii1871 小时前
基于栅格法的机器人工作空间划分系统
数据结构·机器人
wljy12 小时前
二、静态库的制作和使用
linux·c语言·开发语言·c++
道剑剑非道2 小时前
FFmpeg 6.0 实战:用 C++ 封装摄像头采集与 RTSP 推流
开发语言·c++·ffmpeg
天天进步20152 小时前
Python全栈项目实战:基于深度学习的语音合成(TTS)系统
开发语言·python·深度学习
OctShop大型商城源码3 小时前
.NET线上商城源码_C#商城源码_技术赋能下的电商新生态
开发语言·c#·.net·商城系统源码
IT猿手3 小时前
光伏模型参数估计:基于山羊优化算法(GOA )的光伏模型参数辨识问题求解研究,免费提供完整MATLAB代码链接
开发语言·算法·matlab·群智能优化算法·智能优化算法·光伏模型参数估计·光伏模型参数辨识
xrgs_shz3 小时前
【高光谱数据处理实战】基于Python的ENVI图像交互式裁剪与光谱数据预处理
开发语言·图像处理·python