【数据结构】堆详解!(图解+源码)

个人头像
🎥 屿小夏 : 个人主页
🔥个人专栏 : 数据结构解析
🌄 莫道桑榆晚,为霞尚满天!

文章目录

  • 🌤️前言
  • 🌤️堆的理论
    • ☁️二叉树的顺序存储
    • ☁️堆的概念
  • 🌤️堆的实现逻辑
    • ☁️堆向下调整算法
    • ☁️建堆
    • ☁️建堆时间复杂度
    • ☁️堆的插入
    • ☁️堆的删除
  • 🌤️堆的代码是实现
    • ☁️堆的结构体
    • ☁️堆的初始化
    • ☁️堆的销毁
    • ☁️堆的插入
    • ☁️堆的删除
    • ☁️取堆顶数据
    • ☁️堆的数据个数
    • ☁️堆的判空
  • 🌤️堆特性总结
  • 🌤️全篇总结

在这里插入图片描述

🌤️前言

堆是一种基本而强大的数据结构。本文将深入探讨堆的概念、原理以及实现。

🌤️堆的理论

☁️二叉树的顺序存储

普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。

在这里插入图片描述

☁️堆的概念

在这里插入图片描述

堆的性质:

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

在这里插入图片描述

🌤️堆的实现逻辑

☁️堆向下调整算法

一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整
成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

int array[] = {27,15,19,18,28,34,65,49,25,37};

在这里插入图片描述

☁️建堆

给定一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,这个时候就需要我们通过算法,把它构建成一个堆。根节点左右子树不是堆,我们从倒数的第一个非叶子节点的子树开始调整,一直调整到根节点的树,就可以调整成堆(向下调整)。

int a[] = {1,5,3,8,7,6};

在这里插入图片描述

☁️建堆时间复杂度

因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来看的就是近似值,多几个节点不影响最终结果)

在这里插入图片描述

根据上图可以推算出: 建堆的时间复杂度为O(N)。

☁️堆的插入

先插入一个10到数组的尾上,再进行向上调整算法,直到满足堆。

在这里插入图片描述

☁️堆的删除

删除堆是删除堆顶的数据,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。

在这里插入图片描述

🌤️堆的代码是实现

☁️堆的结构体

typedef int HeapDataType;

typedef struct Heap
{
	HeapDataType* a;
	int size;  //有效元素
	int cpciti; //容量
}HP;
  • HeapDataType 定义了堆中元素的数据类型,这里是整数。
  • struct Heap 定义了一个包含堆数据的结构体,包括一个指向堆数组的指针 ,堆的有效元素个数 ,以及堆的容量 。

☁️堆的初始化

void HeapInit(HP* hp)
{
	assert(hp);
	hp->a = NULL;
	hp->size = hp->cpciti = 0;
}

首先使用 断言来确保传入的指针 不为空。然后,将堆数组指针设置为 NULL,将堆的有效元素个数和容量都初始化为 0。

☁️堆的销毁

void HeapDestroy(HP* hp)
{
	assert(hp);
	free(hp->a);
	hp->a = NULL;
	hp->size = hp->cpciti = 0;
}

使用 断言确保传入的指针 不为空。然后,使用函数释放堆数组分配的内存,并将指针设置为 NULL。最后,将堆的有效元素个数和容量都设置为 0。

☁️堆的插入

void AdjustUp(HeapDataType* a, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
void HeapPush(HP* hp, HeapDataType x)
{
	assert(hp);
	//
	if (hp->size == hp->cpciti)
	{
		int newCapacity = hp->cpciti == 0 ? 4 : hp->cpciti * 2;
		HeapDataType* tmp = (HeapDataType*)realloc(hp->a, sizeof(HeapDataType) * newCapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		hp->a = tmp;
		hp->cpciti = newCapacity;
	}
	hp->a[hp->size] = x;
	hp->size++;
	AdjustUp(hp->a, hp->size-1);
}

AdjustUp用于将堆的最后一个节点(即插入的新节点)向上调整,使得以新节点为叶子节点的子树仍然满足堆的性质。具体步骤如下:

  1. 初始化parent为(child - 1) / 2,即新节点的父节点。
  2. 如果child大于0(即child不是根节点),则执行以下操作:
    • 如果child节点的值小于parent节点的值,则交换child和parent节点的值,并更新child为parent,parent为(child - 1) / 2。
    • 否则,跳出循环。
  3. 调整结束。

HeapPush用于向堆中插入一个新的元素。具体步骤如下:

  1. 检查堆的大小是否达到了容量上限,如果是,则进行扩容操作。
  2. 将新元素x放入堆的最后一个位置。
  3. 堆的大小加1。
  4. 调用AdjustUp函数,将新插入的元素向上调整。

☁️堆的删除

void AdjustDown(HeapDataType* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child + 1 < n && a[child + 1] < a[child])
		{
			child++;
		}
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
void HeapPop(HP* hp)
{
	assert(hp);
	assert(hp->size > 0);

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

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

AdjustDown用于将堆的根节点向下调整,使得以根节点为根的子树仍然满足堆的性质。具体步骤如下:

  1. 初始化child为parent的左孩子节点。
  2. 如果child小于n(即child在数组范围内),则执行以下操作:
    • 如果child+1也小于n且右孩子节点的值小于左孩子节点的值,则将child更新为右孩子节点。
    • 如果child节点的值小于parent节点的值,则交换child和parent节点的值,并更新parent为child,child为parent的左孩子节点。
    • 否则,跳出循环。
  3. 调整结束。

HeapPop用于删除堆的根节点。具体步骤如下:

  1. 交换根节点和最后一个节点的值。
  2. 将堆的大小减1。
  3. 调用AdjustDown函数,将根节点向下调整。

☁️取堆顶数据

HeapDataType HeapTop(HP* hp)
{
	assert(hp);
	assert(hp->size > 0);
	return hp->a[0];
}

断言来确保传入的指针 是非空的(不为 NULL),以及堆的大小大于0。如果这些条件不满足,程序会终止执行。然后,返回堆的顶部元素,也就是堆数组中的第一个元素。

☁️堆的数据个数

int HeapSize(HP* hp)
{
	return hp->size;
}

size即堆的大小,表示堆中当前包含的元素个数。

☁️堆的判空

int HeapEmpty(HP* hp)
{
	assert(hp);
	return hp->size == 0;
}

断言确保传入的指针不为空,检查堆的大小是否等于0。如果堆的大小为0,函数返回1(表示堆为空),否则返回0(表示堆不为空)。

🌤️堆特性总结

  1. 堆是一棵完全二叉树,即除了最后一层外,其他层都是满的,最后一层从左到右填满。
  2. 堆分为大根堆和小根堆两种,大根堆中每个节点的值都大于其子节点的值,小根堆中每个节点的值都小于其子节点的值。
  3. 堆的根节点是堆中的最小(或最大)元素。
  4. 堆中的任意节点的值都小于(或大于)其子节点的值。
  5. 堆中的元素是按照层序遍历的顺序存储在数组中的,可以用数组来实现堆。
  6. 堆的插入和删除操作分别为向上调整(AdjustUp)和向下调整(AdjustDown),保证插入和删除后仍然满足堆的性质。
  7. 堆的时间复杂度为O(logN),其中N为堆中元素的个数。

🌤️全篇总结

堆作为数据结构中的重要部分,展现了在多种算法和应用中的价值。掌握堆的知识会对你以后解决各种问题和优化性能提供重要帮助。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java类和对象(续)

书接上回我们已经学完了对象的初始化&#xff0c;今天的内容更加精彩。 1.封装 面向对象程序的三大特征&#xff1a;封装&#xff0c;继承&#xff0c;多态。 本章主要也是要研究封装&#xff0c;简单来说就是套壳屏蔽细节。 封装的概念&#xff1a; 封装&#xff1a;将数据和…

Redis系列-Redis安装与配置【2】

目录 Redis系列-Redis安装与配置【2】二、Redis安装与配置Redis安装步骤windowDocker安装 Redis配置文件说明Redis启动和停止命令启动Redis服务打开Redis客户端进行连接 使用可视化工具Another Redis Desktop ManagerRedisInsight 个人主页: 【⭐️个人主页】 需要您的【&#…

element-plus 循环生成的多个input输入框如何校验

我们知道正常写出来的input输入框如何校验&#xff0c;那循环出来的输入框应该怎么校验咧&#xff0c;这里就教大家如何的去校验通过循环出来的输入框。 首先先看单个的input如何做校验 <template><div><el-form ref"ruleFormRef" :model"ruleF…

通过海康私有协议Ehome/ISUP协议将海康摄像头、录像机等设备统一接入到LiveNVR Web流媒体平台实现统一汇聚及Web播放等的配置说明,

LiveNVR海康摄像头海康NVR通过EHOME协议ISUP协议接入支持转GB28181级联 1、海康 ISUP 接入配置2、海康设备接入2.1、海康EHOME接入配置示例2.2、海康ISUP接入配置示例 3、通道配置3.1、直播流接入类型 海康ISUP3.2、海康 ISUP 设备ID3.3、启用保存3.4、接入成功 4、相关问题4.1…

K8S容器内安装cur/telnet命令(Alpine Linux离线环境安装curl/telnet或其他工具)

背景 需求&#xff1a; 微服务的基础是镜像&#xff0c;通常在最小化的Linux镜像中安装jdk&#xff0c;然后运行编译好的java程序。将镜像运行到K8S上就得到了微服务Pod&#xff0c;Pod通常使用安装K8S时配置的私有网段&#xff0c;与宿主机不同。很多时候需要排查从Pod网段内…

Hadoop学习总结(使用Java API操作HDFS)

使用Java API操作HDFS&#xff0c;是在安装和配置Maven、IDEA中配置Maven成功情况下进行的&#xff0c;如果Maven安装和配置不完全将不能进行Java API操作HDFS。 由于Hadoop是使用Java语言编写的&#xff0c;因此可以使用Java API操作Hadoop文件系统。使用HDFS提供的Java API构…

Delphi 12 重返雅典 (RAD Studio 12)

RAD Studio 12 的新功能&#xff1a; 以最新的平台版本为目标&#xff01; RAD Studio 12 提供对 iOS 17&#xff08;仅适用于 Delphi&#xff09;、Android 14 和 macOS Sonoma 的官方支持。RAD Studio 12 还支持 Ubuntu 22 LTS 和 Windows Server 2022。 Delphi 源代码的多…

小黑子—springMVC:第一章 请求处理与响应数据

springMVC入门1.0 1、小黑子的springMVC基础1.1 SpringMVC概述1.2 SpringMVC快速入门1.3 Controller中直接注入spring中维护的Bean1.4 SpringMVC关键组件浅析 2、SpringMVC的请求处理2.1 请求映射路径配置2.2 请求数据的接收2.2.1 键值对方式接收数据2.2.1 - I RquestParam属性…

华为云Ascend310服务器使用

使用华为云服务器 cpu: 16vCPUs Kunpeng 920 内存&#xff1a;16GiB gpu&#xff1a;4* HUAWEI Ascend 310 cann: 20.1.rc1 操作系统&#xff1a;Ubuntu aarch64目的 使用该服务器进行docker镜像编译&#xff0c;测试模型。 已知生产环境&#xff1a;mindx版本为3.0.rc3&a…

有符号数是如何判断正负符号位的?

文章目录 有符号数是如何判断正负符号位的&#xff1f; 运行结果&#xff1a; 有符号数是如何判断正负符号位的&#xff1f; #include<stdio.h> int main() {int input_data 0;printf("Please input the data ! \n");scanf("%d",&input_data);…

【CASS精品教程】cass3d基于osgb根据点、线、面提取高程,生成等高线

本文讲解cass3d 11.0基于osgb三维模型,根据点、线、面提取高程点,并将高程点保存到文件。cass11.0下载与安装请点击。 一、加载osgb模型 打开cass11.0软件,打开3d窗口,加载osgb三维模型,如下图所示。 二、点提取 CASS3d中提取点高程的快捷键是G,输入G命令,回车。 提示…

5 Paimon数据湖之表数据查询详解

更多Paimon数据湖内容请关注&#xff1a;https://edu.51cto.com/course/35051.html 虽然前面我们已经讲过如何查询Paimon表中的数据了&#xff0c;但是有一些细节的东西还需要详细分析一下。 首先是针对Paimon中系统表的查询&#xff0c;例如snapshots\schemas\options等等这些…

C# 同步异步大白话

同步异步大白话 背景 任务异步编程模型&#xff08;TAP&#xff09;提供了对异步代码的抽象。您可以像往常一样&#xff0c;将代码编写为一系列语句。您可以阅读该代码&#xff0c;就好像每条语句都在下一条语句开始之前完成一样。编译器执行许多转换&#xff0c;因为其中一些…

log4j CVE-2021-44228 RCE漏洞复现

一、漏洞特征 Apache Log4j 是 Apache 的一个开源项目&#xff0c;Apache Log4j2是一个基于Java的日志记录工具。该工具重写了Log4j框架&#xff0c;并且引入了大量丰富的特性。我们可以控制日志信息输送的目的地为控制台、文件、GUI组件等&#xff0c;通过定义每一条日志信息的…

双编码器构建机器人零力拖动/导纳控制思路

前言 这篇博客主要记录昨日与实验室大佬针对UR5机器人拖动示教功能实现的思路。由于本人并非主攻力控方面。直到昨天在做实验的时候&#xff0c;与力控组的大佬讨论过后才了解UR机器人实现导纳控制的思路。 关于导纳控制/零力拖动 导纳控制与阻抗控制单从字面去理解很容易记…

多因素验证如何让企业邮箱系统登录更安全?

企业邮箱系统作为基础的办公软件之一&#xff0c;既是企业内外沟通的重要工具&#xff0c;也是连接企业多个办公平台的桥梁&#xff0c;往往涉及到客户隐私、业务信息、企业机密等等。为了保护邮箱账户的安全&#xff0c;设置登陆密码无疑是保护账户安全的常用措施之一。然而随…

如何设计一个网盘系统的架构

1. 概述 现代生活中已经离不开网盘&#xff0c;比如百度网盘。在使用网盘的过程中&#xff0c;有没有想过它是如何工作的&#xff1f;在本文中&#xff0c;我们将讨论如何设计像百度网盘这样的系统的基础架构。 2. 系统需求 2.1. 功能性需求 用户能够上传照片/文件。用户能…

一题三解(暴力、二分查找算法、单指针):鸡蛋掉落

涉及知识点 暴力、二分查找算法、单指针 题目 给你 k 枚相同的鸡蛋&#xff0c;并可以使用一栋从第 1 层到第 n 层共有 n 层楼的建筑。 已知存在楼层 f &#xff0c;满足 0 < f < n &#xff0c;任何从 高于 f 的楼层落下的鸡蛋都会碎&#xff0c;从 f 楼层或比它低的…

归并分治 归并排序的应用 + 图解 + 笔记

归并分治 前置知识&#xff1a;讲解021-归并排序 归并排序 图解 递归 非递归 笔记-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/134338789?spm1001.2014.3001.5501原理&#xff1a; (1&#xff09;思考一个问题在大范围上的答案&#xff0c;是否等于&…

postman 参数化使用csv导入外部数据

一、参数化脚本入参 postman中变量用{{变量名}}表示变量 二、创建外部数据文件 csv文件逗号分割多个变量和对应值注意编码格式必须为utf-8 三、run collection导入数据文件 四、设置运行参数run 浏览数据 可调试设置迭代次数&#xff1a;防止批量出错&#xff0c;可先设定…