堆(C语言实现)

news2024/11/25 2:27:52

文章目录:

  • 1.堆的概念
  • 2.堆的性质
  • 3.堆的结构
  • 4.接口实现
    • 4.1初始化堆
    • 4.2销毁堆
    • 4.3打印堆内元素
    • 4.4向上调整
    • 4.5向堆中插入数据
    • 4.6向下调整
    • 4.7删除堆顶元素
    • 4.8查看堆顶元素
    • 4.9统计堆内数据个数
    • 4.10判断堆是否为空
    • 4.11堆的构建

1.堆的概念

如果有一个关键码的集合,把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,孩子节点都不大于父节点的称为小堆,孩子节点都不大于其父节点的称为大堆

图示:

由此可以看出:任何数组都可以看做完全二叉树,但不一定是堆

2.堆的性质

1.堆中某个节点的值总是不大于或不小于其父节点的值
2.堆总是一棵完全二叉树
3.孩子节点和父亲节点下标的关系:

  • parent = (child - 1) / 2
  • leftchild = parent * 2 + 1
  • rightchild = parent * 2 + 2

3.堆的结构

堆总是一棵完全二叉树,堆是用数组来存储的

typedef int HPDataType;//数据类型
typedef struct Heap
{
	HPDataType* a;//数组
	int size;//数据个数
	int capacity;//容量
}HP;

4.接口实现

4.1初始化堆

将数组指针指向NULL,将数据个数和容量置零方便后面统计和判断容量,这里断言php是防止人为不小心传入了空指针

//初始化堆
void HeapInit(HP* php)
{
	assert(php);
	php->a = NULL;// 指空
	php->size = php->capacity = 0;// 置零
}

4.2销毁堆

释放掉数组,并将数组指针指空,将数据个数和容量置零即可

//销毁堆
void HeapDestroy(HP* php)
{
	assert(php);
	free(php->a);//释放
	php->a = NULL;//指空
	php->size = php->capacity = 0;//置零
}

4.3打印堆内元素

遍历数组将其内的元素打印出来即可

//打印堆内元素
void HeapPrint(HP* php)
{
	assert(php);	
	//遍历打印
	for(int i = 0; i < php->size; ++i)
	{
		printf("%d ", php->a[i]);
	}
	printf("\n");
}

4.4向上调整

将孩子节点的数据和其父结点的数据进行比较,若孩子节点的数据大于(小于)父节点的数据就交换并继续向上调整,直到根结点(数组中下标为0的元素)位置就停止,这里对父子节点的比较是根据堆是大堆还是小堆来决定的

//交换函数
void Swap(HPDataType* p1, HPDataType* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

//向上调整
void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		//孩子大于(小于)父亲,就交换并继续向上调整,反之则调整结束
		//if (a[child] > a[parent])
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

4.5向堆中插入数据

首先创建一个新节点,并判断数组是否需要扩容,然后直接向数组尾部插入一个数据,将该数据向上调整(保证结构继续是个堆)即可

//向堆中插入数据
void HeapPush(HP* php, HPDataType x)
{
	assert(php);
	//判断扩容
	if (php->size == php->capacity)
	{
		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++;//数据个数+1
	AdjustUp(php->a, php->size - 1);//从尾节点开始向上调整
}

4.6向下调整

将孩子节点的数据和其父结点的数据进行比较,若孩子节点的数据大于(小于)父节点的数据就交换并继续向小调整,直到最后一个节点(数组中的最后一个的元素)就停止,这里对父子节点的比较是根据堆是大堆还是小堆来决定的

//向下调整
void AdjustDown(HPDataType* a, int n, int parent)
{
	int child = parent * 2 + 1;//默认是左孩子
	while (child < n)
	{
		//确认child指向大的(小的)那个孩子
		//if (a[child + 1] > a[child] && child + 1 < n)
		if (a[child + 1] < a[child] && child + 1 < n)
		{
			++child;//换成右孩子
		}
		//孩子大于(小于)父亲,就交换并继续向下调整,反之则调整结束
		//if (a[child] > a[parent])
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

4.7删除堆顶元素

将堆顶元素和堆尾数据元素交换并删除堆尾(此时堆尾数据也就是堆顶数据),再从根节点开始向下调整(保证它继续是个堆)即可,这里需要断言堆为空的情况,为空不能删

//删除堆顶元素
void HeapPop(HP* php)
{
	assert(php);
	//断言堆为空
	assert(php->size > 0);
	//交换堆顶元素和堆尾元素
	Swap(&php->a[0], &php->a[php->size - 1]);
	php->size--;//数据个数-1
	AdjustDown(php->a, php->size, 0);//从根节点开始向下调整
}

4.8查看堆顶元素

数组首元素就是堆顶元素,直接将其返回即可,这里需要断言堆为空的情况,为空无堆顶元素

//查看堆顶元素
HPDataType HeapTop(HP* php)
{
	assert(php);
	//断言堆为空
	assert(php->size > 0);
	//返回数组首元素(堆顶元素)
	return php->a[0];
}

4.9统计堆内数据个数

结构体中的size就是有效数据的个数,直接将其返回即可

//统计堆内数据个数
int HeapSize(HP* php)
{
	assert(php);
	//返回有效数据个数
	return php->size;
}

4.10判断堆是否为空

如果堆内的有效数据个数为0,则堆为空,直接返回其布尔值即可

//判断堆是否为空
bool HeapEmpty(HP* php)
{
	assert(php);
	//有效数据个数为0则堆为空
	return php->size == 0;
}

4.11堆的构建

我们可以人为传入一个数组来建堆,首先需要开辟一个与传入的数组大小相同的空间,用来将数组中的内容复制到该空间中,然后从最后一个元素的父节点开始((size-1-1)/2)依次向前可以遍历到每颗子树的父节点,并对每颗子树向下调整

//堆的构建
void HeapCreate(HP* php, HPDataType* a, int n)
{
	assert(php);
	php->a = (HPDataType*)malloc(sizeof(HPDataType) * n);
	if (php->a == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	//将数组复制到开辟的空间中
	memcpy(php->a, a, sizeof(HPDataType) * n);
	php->size = php->capacity = n;

	//建堆算法
	//从最后一个元素的父节点开始依次向前可以遍历到每颗子树的父节点
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(php->a, n, i);
	}
}

图解:


堆的实现到这里就介绍结束了,期待大佬们的三连!你们的支持是我最大的动力!
文章有写的不足或是错误的地方,欢迎评论或私信指出,我会在第一时间改正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/48732.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Redis】缓存更新策略

1. 缓存更新策略综述 内存淘汰 不用自己维护&#xff0c;利用 Redis 自己的内存淘汰机制 &#xff08;内存不足时&#xff0c;触发策略&#xff0c;默认开启&#xff0c;可自己配置&#xff09;&#xff0c;其可在一定程度上保持数据一致性 超时剔除 给数据添加 TTL&#x…

【电力运维】浅谈电力通信与泛在电力物联网技术的应用与发展

摘要&#xff1a;随着我国社会经济的快速发展&#xff0c;我国科技实力得到了巨大的提升&#xff0c;当前互联网通信技术在社会中得到了广泛的应用。随着电力通信技术的快速发展与更新&#xff0c;泛在电力物联网建设成为电力通讯发展的重要方向。本文已泛在电力物联网系统为核…

Docker使用

xshell和xftp软件下载 链接&#xff1a;https://pan.baidu.com/s/1G7DIw14UvOmTwU9SwtYILg 提取码&#xff1a;he18 --来自百度网盘超级会员V6的分享 docker相关资料&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1VcxvuJvBIKNKnUUHPlM3MA 提取码&#xff1a;6w5e …

一些常见的项目管理 KPI

本文将介绍一些常见的项目管理kpi&#xff0c;让大家更深刻的了解其作用及所存在的问题。 一、关键绩效指标的作用 在 GPS 和其他现代导航方法出现之前&#xff0c;水手和探险家们只能通过星星找到正确的方向。特别是在北半球&#xff0c;他们利用北极星找出真正的北方方位。…

[附源码]SSM计算机毕业设计医学季节性疾病筛查系统JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Mysql高频面试题(一)

文章目录1. Mysql如何实现的索引机制&#xff1f;2. InnoDB索引与MyISAM索引实现的区别是什么&#xff1f;3. 一个表中如果没有创建索引&#xff0c;那么还会创建B树吗&#xff1f;4. B树索引实现原理&#xff08;数据结构&#xff09;5. 聚簇索引与非聚簇索引的B树实现有什么区…

Vector源码分析

Vector源码分析 1 Vector基本介绍与类图 Vector 类实现了一个动态数组。和 ArrayList 很相似,但是两者是不同的: Vector 是同步访问的。Vector 包含了许多传统的方法,这些方法不属于集合框架。Vector 主要用在事先不知道数组的大小,或者只是需要一个可以改变大小的数组的…

pytest + yaml 框架 - 1.我们发布上线了!

前言 基于 httprunner 框架的用例结构&#xff0c;我自己开发了一个pytest yaml 的框架&#xff0c;那么是不是重复造轮子呢&#xff1f; 不可否认 httprunner 框架设计非常优秀&#xff0c;但是也有缺点&#xff0c;httprunner3.x的版本虽然也是基于pytest框架设计&#xff…

Spring中JDK与Cglib动态代理的区别

靠Spring吃饭的小伙伴一定经常听说动态代理这个词&#xff0c;没错&#xff0c;Aop就是靠它来实现的。Spring提供了两种代理模式&#xff1a;JDK动态代理、Cglib动态代理&#xff0c;供我们选择&#xff0c;那他们有啥区别呢&#xff1f;Sping为啥不自己从中挑选一个作为代理模…

IB物理的费曼图怎么考?

费曼图是用来描述基本粒子间相互作用的图形化表示&#xff0c;由诺贝尔物理学奖得主、著名物理学家理查德费曼&#xff08;Richard Feynman&#xff09;提出&#xff0c;十分清晰直观。虽然真正的费曼图可以用来做更深奥的数学计算&#xff0c;但是在IB物理中&#xff0c;考纲要…

那些惊艳一时的 CSS 属性

1.position: sticky 不知道大家平时业务开发中有没有碰到像上图一样的吸顶的需求&#xff1a;标题在滚动的时候&#xff0c;会一直贴着最顶上。 这种场景实际上很多&#xff1a;比如表格的标题栏、网站的导航栏、手机通讯录的人名首字母标题等等。如果让大家自己动手做的话&…

flink学习

Flink学习之路&#xff08;一&#xff09;Flink简介 - 走看看 Flink(一)-基本概念 - 知乎 Flink架构&#xff1a; Flink整个系统包含三个部分&#xff1a; 1、Client&#xff1a; 给用户提供向Flink系统提交用户任务&#xff08;流式作业&#xff09;的能力。用户提交一个F…

大型商场借力泛微,实现内外协同招商,合同、铺位、费用统一管理

对即将开业或是面临调整改造的购物中心来说&#xff0c;用什么样的方式才能快速地达成招商目的&#xff0c;实现资产价值的保值和增值&#xff0c;成为商业操盘手们共同面临的难题…… 行业需求 • 建立充足的品牌资源储备&#xff0c;拓宽招商渠道和线索&#xff0c;提高成交…

ElasticSearch-全文检索和分析引擎学习Day01

前言 学习谷粒商城基础片完结后便开启了高级部分的学习&#xff0c;高级部分的第一章节 Elasticsearch 搜索和分析引擎。文档地址&#xff1a;elasticsearch中文文档地址 一、Elasticsearch 简介 1.1 Elasticsearch 是什么&#xff1f; Elasticsearch 是一个分布式的免费开…

入门力扣自学笔记208 C++ (题目编号:895)

895. 最大频率栈​​​​​​ 题目&#xff1a; 设计一个类似堆栈的数据结构&#xff0c;将元素推入堆栈&#xff0c;并从堆栈中弹出出现频率最高的元素。 实现 FreqStack 类: FreqStack() 构造一个空的堆栈。 void push(int val) 将一个整数 val 压入栈顶。 int pop() 删除…

Leetcode 85.最大矩形(困难)

一、题目 1、题目描述 给定一个仅包含 0 和 1 、大小为 rows x cols 的二维二进制矩阵&#xff0c;找出只包含 1 的最大矩形&#xff0c;并返回其面积。 示例1&#xff1a; 输入&#xff1a;matrix [["1","0","1","0","0&qu…

基于微信小程序奶茶店在线点单管理系统ssm框架-计算机毕业设计

面对目前奶茶店林立的现状&#xff0c;大城市奶茶店多为连锁奶茶店他们都有统一的管理和相应的系统。但是个别小县城和小城以及城区也有不少的奶茶店多为自营&#xff0c;这就必须店长自己管理和采购原料。大型连锁的奶茶店管理系统就不适用于分散的小型奶茶店。小型奶茶店的管…

剑指 Offer 10- II. 青蛙跳台阶问题

一、题目描述 一只青蛙一次可以跳上1级台阶&#xff0c;也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。 答案需要取模 1e97&#xff08;1000000007&#xff09;&#xff0c;如计算初始结果为&#xff1a;1000000008&#xff0c;请返回 1。 二、示例 示…

TaWRKY19/61/82激活糖转运蛋白TaSTP3从而增强小麦条锈病敏感性

文章信息 题目&#xff1a;Sugar transporter TaSTP3 activation by TaWRKY19/61/82 enhances stripe rust susceptibility in wheat 刊名&#xff1a;New Phytologist 作者&#xff1a;Baoyu Huai&#xff0c;Zhensheng Kang,Jie Liu et al. 单位&#xff1a;Northwest A&…

【C++】string使用模拟实现

文章目录前言为什么学习string类&#xff1f;1. string函数常用接口介绍1.1 string容器基本概念1.2 string构造函数1.3 string访问和修改1.4 string插入和删除1.5 string赋值操作1.5 string字符串拼接1.7 string查找和替换1.8 string子串1.9 string类对象的容量操作2. string类…