堆的基本实现

news2024/11/15 3:32:59

一、堆的概念

在提出堆的概念之前,首先要了解二叉树的基本概念

一颗二叉树是节点的有限集合,该集合:

1、或者为空;

2、或者由一个根节点加上两颗分别称为左子树和右子树的两颗子树构成;

 堆就是一颗完全二叉树;

堆有两种:小堆和大堆

堆在内存上的存储是数组形式的(物理结构);我们认为想象成链式结构(逻辑结构)

通过数组结构去实际存储,有这样的规律:每个父节点的下标乘以2加1就是左孩子节点,加2就是右孩子节点;无论是左孩子还是右孩子,其下标减去1再 /2 就是父节点的下标,这就是通过数组存储堆(完全二叉树)的优点。


 

 二、堆实现的相关头文件

#include<stdio.h>
#include<assert.h>
#include<stdbool.h>
#include<stdlib.h>
#include<errno.h>
//堆有两种:大堆、小堆
typedef int HPDataType;

typedef struct Heap
{
	HPDataType* arr;
	int size;
	int capacity;
}Heap;

//堆的构建
void HeapInit(Heap* php);

//堆的销毁
void HeapDestroy(Heap* php);

//向上调整
void AdjustUp(Heap* php, int child);
//堆的插入
void HeapPush(Heap* php, HPDataType x);

//向下调整
void AdjustDown(Heap* php, int parent, int size);
//堆的删除
void HeapPop(Heap* php);

//取出堆顶的数据
HPDataType HeapTop(Heap* php);

//堆的数据个数
int HeapSize(Heap* php);

//堆的判空
bool HeapEmpty(Heap* php);

堆是完全二叉树,所以用数组存储可以方便访问子节点和父节点;


三、堆基本接口的实现(大堆)

1、堆的构建(初始化)

void HeapInit(Heap* php)
{
	assert(php);
	php->arr = NULL;
	php->size = php->capacity = 0;
}

2、堆的销毁

//堆的销毁
void HeapDestroy(Heap* php)
{
	assert(php);
	if (php->arr == NULL)
	{
		free(php->arr);
	}
	php->arr = NULL;
	php->size = php->capacity = 0;
}

3、堆的插入

void HeapPush(Heap* php, HPDataType x)
{
	assert(php);
	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;
		HPDataType* tmp = (HPDataType*)realloc(php->arr,sizeof(HPDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc failed");
			exit(1);
		}
		php->arr = tmp;
		php->capacity = newcapacity;
	}
	php->arr[php->size++] = x;

	//向上调整
	AdjustUp(php->arr,php->size-1);
}

堆只有大堆和小堆之分,插入数据和顺序表一样,关键是插入数据之后要对数组里面的数据进行调整;

插入数据用到向上调整

//向上调整
void AdjustUp(HPDataType* arr,int child)
{
	int parent = (child - 1) / 2;
	while (child>0)
	{
		if (arr[child] > arr[parent])
		{
			//交换数组里面的值
			Swap(&arr[child], &arr[parent]);
			//继续比较大小要将child和parent的值向上移动
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			//之前的数据都是一个一个建好的大堆
			//父节点的值比子节点的大,停止
			break;
		}
	}
}
void Swap(HPDataType* x, HPDataType* y)
{
	HPDataType tmp = *x;
	*x = *y;
	*y = tmp;
}

每次插入进堆的数据都是孩子节点(形参名称),向上调整的原因就是每个子树的父亲节点理论上是要大于孩子节点的,但是插入的数据可能要比父节点大,所以在向上调整函数里面要先通过孩子节点的下标找到对应的父节点,之后再比较看是否要交换,直到父亲节点大于子节点;

循环结束的条件是child>0,当child为0的时候说明已经调整到根节点的位置了。

4、堆的删除

//堆的删除
void HeapPop(Heap* php)
{
	assert(php && php->size>0);
	//删除是删除的是堆顶的数据,若是直接让数组整体往前移动 堆被打乱 要重新建堆 时间复杂度高;
	//方法:交换堆首位的数据,让size--,再从堆顶开始向下调整
	Swap(&php->arr[0],& php->arr[php->size - 1]);
	php->size--;
	AdjustDown(php->arr, 0, php->size - 1);
}

堆的删除删除的是根节点的数据,也就是数组里面下标为0的数据;如果直接移动数组下标删除,那么新数组就不再是一个堆,此时要重新建堆,代价过大;

采用这种方式:每次进行删除之前先交换堆首尾的数据,之后再让size--,就访问不到原来的根节点,此时得到的新数组,除了根节点处的数据,它的子树都是大堆;此时进行向下调整算法,把这个不应该是根节点的数据向下调整,把下面的数据里面最大的调整到根节点处;

向下调整算法

void AdjustDown(HPDataType* arr, int parent, int size)//size指向的是最后一个有效数据
{
	int child = parent * 2 + 1;//定义为左孩子
	while (child <= size)
	{
		if (child+1<=size && arr[child] < arr[child + 1])//当最后一个子树只有左孩子时,child+1越界
		{
			child = child + 1;//若是右孩子较大,那么就定义成右孩子
		}//总是大的调到上面去

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

向下调整是从下标为0处开始调整的,这个0下标位置就是父节点,通过乘以2加上1的办法找到子节点,但是要找到子节点里面较大的那个所以上面代码就有了child处和child+1处数据大小的比较,若是父节点小于子节点就交换;每次调整完都刷新父节点和子节点的下标,以便一直往下面调整直到父节点的数据要大于子节点的数据就停止;

循环结束的条件是child<size,这里的size是数组里面最后一个有效数的下标;

5、堆顶数据、堆的数据个数、堆的判空

//取出堆顶的数据
HPDataType HeapTop(Heap* php)
{
	assert(php && php->size>0);
	return php->arr[0];
}

//堆的数据个数
int HeapSize(Heap* php)
{
	assert(php);
	return php->size;
}

//堆的判空
bool HeapEmpty(Heap* php)
{
	assert(php);
	return php->size == 0;
}

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

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

相关文章

Python学习笔记45:游戏篇之外星人入侵(六)

前言 飞船模块的功能基本已经完成。今天继续完成子弹模块的功能。 子弹模块 子弹和飞船模块&#xff0c;在游戏逻辑中有一种生成与被生成的表面关系&#xff0c;因为子弹在游戏中是由飞船发射的。但是在我们实际抽象的过程中&#xff0c;飞船与子弹并不是is的关系&#xff0…

【机器学习】机器学习之多变量线性回归-Multiple_Variable_Soln

引言 扩展数据结构和之前开发的例程&#xff0c;以支持多个特征。有几个例程被更新&#xff0c;使得实验看起来有些冗长&#xff0c;但实际上只是对之前的例程进行了小的调整&#xff0c;因此快速回顾是可行的 文章目录 引言一、多变量线性回归1.1 目标1.2 工具 二、问题陈述2.…

2024年【山东省安全员C证】考试题及山东省安全员C证复审考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年山东省安全员C证考试题为正在备考山东省安全员C证操作证的学员准备的理论考试专题&#xff0c;每个月更新的山东省安全员C证复审考试祝您顺利通过山东省安全员C证考试。 1、【多选题】.以下属于建设单位的质量行…

【SpringBoot教程:从入门到精通】掌握Springboot开发技巧和窍门(四)-Vue项目配置环境、导航栏

主要写前端页面&#xff0c;采用vue框架写页面的导航栏&#xff01;&#xff01;&#xff01; 文章目录 前言 Vue项目配置环境 安装依赖 创建菜单 总结 前言 主要写前端页面&#xff0c;采用vue框架写页面的导航栏&#xff01;&#xff01;&#xff01; Vue项目配置环境 安装…

[练习]如何使用递归算法?

&#x1f3a5; 个人主页&#xff1a;Dikz12&#x1f525;个人专栏&#xff1a;算法(Java)&#x1f4d5;格言&#xff1a;吾愚多不敏&#xff0c;而愿加学欢迎大家&#x1f44d;点赞✍评论⭐收藏 目录 1. 递归概述 2.汉诺塔问题 题目描述​编辑 题解 代码实现 3…

扫雷小游戏纯后端版

package com.wind;import java.util.Random; import java.util.Scanner;public class ResultLei {static Random random new Random();public static void main(String[] args) {boolean end true;while (end) {System.out.println("请输入你选择的难度对应的数字&#…

虚拟机centos9搭建wordpress

目录 1. 更换yum源更新系统软件包&#xff1a; 1.1备份yum源 1.1.1创建备份目录&#xff1a; 1.1.2移动现有仓库配置文件到备份目录&#xff1a; 1.1.3验证备份&#xff1a; 1.2更换yum源 1.2.1添加yum源 1.2.2删除和建立yum缓存 1.3更新系统软件包 1.4 yum与dnf介绍…

springboot使用Gateway做网关并且配置全局拦截器

一、为什么要用网关 统一入口&#xff1a; 作用&#xff1a;作为所有客户端请求的统一入口。说明&#xff1a;所有客户端请求都通过网关进行路由&#xff0c;网关负责将请求转发到后端的微服务 路由转发&#xff1a; 作用&#xff1a;根据请求的URL、方法等信息将请求路由到…

线程池的理解以及实现线程池

线程池可以干什么&#xff1a; 帮助我们减少线程的创建和销毁提高系统资源的利用率&#xff0c;同时控制并发执行的线程数量便于管理且提高响应速度 线程池的工作流程&#xff1a; 1.创建线程池 线程池的创建是通过 Executors 工厂方法或直接使用 ThreadPoolExecutor 构造函…

遇到Websocket就不会测了?别慌,学会这个Jmeter插件轻松解决....

websocket 是一种双向通信协议&#xff0c;在建立连接后&#xff0c;websocket服务端和客户端都能主动向对方发送或者接收数据&#xff0c;而在http协议中&#xff0c;一个request只能有一个response&#xff0c;而且这个response也是被动的&#xff0c;不能主动发起。 websoc…

C语言 ——— 数组指针的定义 数组指针的使用

目录 前言 数组指针的定义 数组指针的使用 前言 之前有编写过关于 指针数组 的相关知识 C语言 ——— 指针数组 & 指针数组模拟二维整型数组-CSDN博客 指针数组 顾名思义就是 存放指针的数组 那什么是数组指针呢&#xff1f; 数组指针的定义 何为数组指针&#xf…

FastAPI(七十七)实战开发《在线课程学习系统》接口开发-- 课程编辑和查看评论

源码见&#xff1a;"fastapi_study_road-learning_system_online_courses: fastapi框架实战之--在线课程学习系统" 课程编辑 先来看下课程编辑 1.判断是否登录 2.判断课程是否存在 3.是否有权限&#xff08;只有自己可以修改自己的课程&#xff09; 4.名称是否重复…

Redis的操作以及SpringCache框架

目录 一.什么是Redis&#xff1f; 二.Redis的相关知识&#xff1a; 三.如何操作Redis&#xff1f; 1&#xff0c;常用命令&#xff1a; 2.Spring Data Redis &#xff08;1&#xff09; pom.xml 配置&#xff1a; &#xff08;2&#xff09;配置Redis数据源&#xff1a; …

IE11添加收藏、关闭窗口时弹出的对话框字体又大又粗很难看的解决办法

原因已查明&#xff0c;在win7 sp1 32位系统下&#xff0c;安装“2020-01 适用于基于 x86 的系统的 Windows 7 月度安全质量汇总&#xff08;KB4534310&#xff09;”这个更新会导致IE11的窗口字体变大变粗&#xff0c;把这个更新卸载了就可以了&#xff0c;无需重装IE11浏览器…

【芯智雲城】详解智能电机驱动在汽车中的应用

随着汽车系统中传统的机械化设计被电子化设计逐渐取代&#xff0c;电机在汽车电子化系统中扮演的角色越来越重要。例如在汽车的动力系统中&#xff0c;由传统的燃油发动机逐步发展为现在的有刷同步电机、感应电机&#xff0c;真正的实现了新能源车的动力革命&#xff1b;传统的…

论文快过(图像配准|Coarse_LoFTR_TRT)|适用于移动端的LoFTR算法的改进分析 1060显卡上45fps

项目地址&#xff1a;https://github.com/Kolkir/Coarse_LoFTR_TRT 创建时间&#xff1a;2022年 相关训练数据&#xff1a;BlendedMVS LoFTR [19]是一种有效的深度学习方法&#xff0c;可以在图像对上寻找合适的局部特征匹配。本文报道了该方法在低计算性能和有限内存条件下的…

Android AutoSize屏幕适配:适配不同屏幕大小的尺寸,让我们无需去建立多个尺寸资源文件

目录 AutoSize是什么 AutoSize如何使用 一、AndroidautoSize是什么 在开发产品的时候&#xff0c;我们会遇到各种各样尺寸的屏幕&#xff0c;如果只使用一种尺寸去定义控件、文字的大小&#xff0c;那么到时候改起来就头皮发麻。以前使用dime的各种类库&#xff0c;文件太多…

一种提供改进的通道迁移率和高可靠性的SiC沟槽MOSFET概念

来源&#xff1a;A SiC Trench MOSFET concept offering improved channel mobility and high reliability&#xff08;2017 19th European Conference on Power Electronics and Applications (EPE’17 ECCE Europe)&#xff09; 摘要 这项工作讨论了与硅基同类产品相比&…

性能测试工具 - Siege

在快速发展的技术时代&#xff0c;网站和应用的性能对于用户体验和业务成功至关重要。作为测试工程师&#xff0c;找到高效的性能测试工具显得尤为重要。今天&#xff0c;我们来聊聊一个备受推崇的性能测试工具——Siege。 为什么Siege能够在众多性能测试工具中脱颖而出&#x…

C++ 内存与编译问题总结

目录 C内存结构 作用域与生存周期 堆与栈 内存对齐 智能指针 shared_ptr 循环引用问题 编译与链接 内存泄漏 补充问题 include “ ”与<> 大端与小端 C内存结构 C程序内存分区 代码区 文件中所有的函数代码、常量以及字符串常量只读&#xff0c;保护程序不会被…