两篇文章讲透数据结构之堆(一)!

目录

1.堆的概念

2.堆的实现方式

3.堆的功能

4.堆的声明

5.堆的实现

5.1堆的初始化

5.2堆的插入

5.2.1向上调整算法

5.2.2堆的插入 

5.3堆的删除

5.3.1向下调整算法

5.3.2堆的删除

5.4获取堆顶元素

5.5获取堆的元素个数

5.6判断堆是否为空

5.7打印堆

5.8建堆

5.8.1向上调整建堆

5.8.2向下调整建堆

5.9销毁堆


1.堆的概念

堆(Heap)可以被看作一种特殊的数据结构,堆通常是一个可以被看成完全二叉树的数组对象

根据堆的数值的关系,可以将堆分为两类:

  • 若满足任意结点的值>=其子结点的值,则可称为大根堆。
  • 若满足任意结点的值<=其子结点的值,则可称为小根堆。

通俗解释来说:

若是任意一个根结点的值都大于其子结点,则被称为大根堆,因为根大!

若是任意一个根结点的值都小于子结点,则是小根堆,因为根小!

2.堆的实现方式

既然堆是一种二叉树,那么我们就可以采用链式存储或者数组存储来实现这个数据结构。但是考虑到完全二叉树的特性,我们在这里采用数组存储的方式,因为这样既方便访问,也不会浪费空间。

既然使用数组存储,就会用到下标,而我们每次插入和删除,都需要计算父结点和子结点的位置。

现在我们取出大根堆的前五个元素,并总结一下父节点和子结点下标的关系。

我们可以总结出上图红字的规律,现在我们具体推导一下这个规律。

因此,我们可以得到如下结论:

  • 若双亲节点存在,则其下标为(i-1)/2。
  • 若孩子节点存在,左孩子下标为2i+1,右孩子为2i+2。

3.堆的功能

  • 堆的初始化。
  • 堆的插入。
  • 堆的删除。
  • 获取堆顶的元素。
  • 堆的元素个数。
  • 堆的判空。
  • 输出堆。
  • 建堆。
  • 销毁堆

4.堆的声明

因为我们是用数组创建堆的,因此堆的声明和顺序表的声明类似! 

typedef int HpDataType;
typedef struct Heap
{HpDataType* a;//存储数据int size;//大小int capacity;//容量
}Heap

5.堆的实现

5.1堆的初始化

堆的初始化与顺序表的初始化类似。

void HeapInit(Heap* hp)//堆的初始化
{assert(hp);hp->a = NULL;//初始化的时候也可以不开空间hp->capacity = 0;hp->size = 0;//hp->size=hp->capacity=0;
}

5.2堆的插入

我们插入一个值进入堆,是进入到堆目前最后一个元素的后面吗?

其实并不一定,我现在给出大家这么一个场景。

如果目前有如此一个大堆:

现在我们要往里插入一个8,是插入到下标为5的地方吗?

肯定不是的,那么他应该插入到什么地方呢? 

很显然,他应该取代掉5的位置,那么,新的堆就应该是下图:

 那么我们应该如何达到这个效果呢?

    我们刚刚已经计算了父节点和子节点的关系,而由于堆是一个完全二叉树,我们在这里先插入其左结点再插入右结点。而左节点和右节点之间是没有大小关系的,因此我们插入结点之后需要和其父节点比较大小关系,如果比父结点大的话就交换,小的话就不动即可。

    而如果第一次比较会发生交换,由于我们不确定它与更上一层的父结点的大小关系,因此我们还需要继续进行比较,直到不会发生交换为止。

    于是乎,向上调整算法诞生了!

5.2.1向上调整算法

void AdjustUp(Heap* hp, int child)//向上调整
{int parent = (child - 1) / 2;while (child > 0)//循环结束条件是孩子成为了根节点{if (hp->a[child] > hp->a[parent]){//交换swap(&hp->a[child], &hp->a[parent]);//这个函数自己写//更新新的父结点和子结点child = parent;parent = (child - 1) / 2;}//没有发生交换就跳出else{break;}}
}

5.2.2堆的插入 

现在我们就可以着手于完成堆的插入了。

void HeapPush(Heap* hp, HpDataType x)//堆的插入
{assert(hp);//判断是否开空间if (hp->size == hp->capacity){int newcapacity = hp->capacity == 0 ? 4 : hp->capacity*2;HpDataType* tmp = (HpDataType*)realloc(hp->a, newcapacity * sizeof(HpDataType));if (!tmp){perror("realloc fail");exit(1);}hp->a = tmp;hp->capacity = newcapacity;}//更新数值  后置++,先使用,再++hp->a[hp->size++] = x;AdjustUp(hp, hp->size - 1);
}

5.3堆的删除

堆的删除是删除堆顶元素,但是问题又出现了,删除了堆顶元素之后,后面的元素怎么办?顺着继承上去吗?那堆原来的亲属关系就全乱了,属实是父子叔侄变兄弟,闹了个哄堂大孝了。

那么这里我们要怎么处理呢?

我们在这里选择的处理方式是,先将堆顶元素和堆尾元素互换位置,之后直接让数组的长度-1。之后再调整根结点和其子节点之间的关系。调整的方法称为向下调整算法

5.3.1向下调整算法

刚刚已经介绍过了向上调整算法,我们也可以推测出向下调整算法的原理是和父节点和子节点的不断比较,但是这里又出现了一个问题,我们要和左子节点还是右子节点比较?

结论:

  • 大堆,则根结点和子结点中较大的值比较
  • 小堆,则根结点和子结点中较小的值比较

原理:这里以大堆为例,如果我们让根结点和较小的孩子值比较,而且发生了交换的话,那么现在的根结点是小于原来较大的孩子的,有悖于堆的定义。

现在我们实现一下向下调整算法

void AdjustDown(int* 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;}}
}

5.3.2堆的删除

堆的删除的思路刚刚已经说过了,我们现在完成它即可。 

void HeapPop(Heap* hp)//删除堆顶元素
{assert(hp);assert(hp->size > 0);swap(&hp->a[0], &hp->a[hp->size - 1]);hp->size--;AdjustDown(hp->a, hp->size, 0);
}

5.4获取堆顶元素

获取堆顶元素,首先要确保堆存在而且堆内有元素。

我们返回堆顶元素,直接返回数组中的第一个元素即可。

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

5.5获取堆的元素个数

获取堆的元素个数,就是返回堆的size,我们直接返回即可。

int HeapSize(Heap* hp)//堆的大小
{assert(hp);return hp->size;
}

5.6判断堆是否为空

判断堆是否为空,就是判断hp的size成员是否等于0,也是直接返回即可。

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

5.7打印堆

数组的打印谁不会啊?

void HeapDisplay(Heap* hp)//堆的打印
{for (int i = 0; i < hp->size; ++i){printf("%d ", hp->a[i]);}printf("\n");
}

5.8建堆

建堆,就是给一个数组不断的调整,调整成为一个堆。

因此我们要传入的参数为堆、数组、数组长度。

首先,我们要在堆中开辟一块和数组空间等大的空间来存储数组元素。

之后,我们需要将参数中的数组中的元素拷贝到我们创建的空间中。

再之后,我们就可以通过我们的调整算法建堆了。

5.8.1向上调整建堆

向上调整建堆就是采用向上调整算法建堆,在这里我们可以用堆的插入取代上述开辟内存的操作。

void HeapCreatUp(Heap* hp, HpDataType* arr, int n)
{assert(hp && arr);//将每个元素向上调整即可for (int i = 0; i < n; i++){HeapPush(hp, arr[i]);}
}

5.8.2向下调整建堆

向下调整建堆就是采取向下算法建堆,在这里我们首先要给堆开辟一个内存,然后拷贝内存,最后采用向下调整算法建堆即可。 

void HeapCreatDown(Heap* hp, HpDataType* arr, int n)
{assert(hp && arr);//给堆开辟内存HpDataType* tmp = (HpDataType*)malloc(sizeof(HpDataType) * n);if (tmp == NULL){perror("malloc fail!");exit(-1);}hp->a = tmp;//将arr中的元素拷贝到堆中memcpy(hp->a, arr, sizeof(HpDataType) * n);hp->size = n;hp->capacity = n;//         最后一个父节点下标          父节点-1for (int i =((n - 1) - 1) / 2; i >= 0; i--){                   AdjustDown(hp->a, n, i);}
}

5.9销毁堆

与顺序表的销毁方式一样,直接销毁即可。 

void HeapDestroy(Heap* hp)//销毁堆
{assert(hp);free(hp->a);hp->size = hp->capacity = 0;
}

6.堆的头文件

Heap.h

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<assert.h>
#include <stdbool.h>
typedef int HpDataType;
typedef struct Heap
{HpDataType* a;//存储数据int size;//大小int capacity;//容量
}Heap;
void HeapInit(Heap* hp);//堆的初始化
void AdjustUp(Heap* hp, int child);//向上调整
void HeapPush(Heap* hp, HpDataType x);//堆的插入
bool HeapEmpty(Heap* hp);//判断堆是否为空
size_t HeapSize(Heap* hp);//堆的大小
void AdjustDown(int* a, int n, int parent);//向下调整
void HeapPop(Heap* hp);//删除堆顶元素
HpDataType HeapTop(Heap* hp);//获取堆顶元素
void HeapDisplay(Heap* hp);//堆的打印
void HeapDestroy(Heap* hp);//销毁堆
void HeapCreatUp(Heap* hp, HpDataType* arr, int n);//向上调整建堆
void HeapCreatDown(Heap* hp, HpDataType* arr, int n);//向下调整建堆

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

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

相关文章

亚马逊自养号测评环境搭建技巧:打造防关联底层环境的关键步骤

今天我们要聊的是完全由人工操作的自养号方法&#xff0c;相信有过相关经验的朋友们都清楚&#xff0c;在实现自养号的过程中&#xff0c;所使用的 IP 和浏览器究竟有哪些选择&#xff0c;以及可能会遇到哪些问题。 首先&#xff0c;我们来看看市场上现有的 IP 类型以及可能出现…

[LDAP: error code 34 - invalid DN]

目前我的项目版本&#xff1a; Spring版本:5.3.15SpringBoot版本:2.6.3 完整错误 org.springframework.ldap.InvalidNameException: [LDAP: error code 34 - invalid DN]; nested exception is javax.naming.InvalidNameException: [LDAP: error code 34 - invalid DN]at org.s…

zabbix实现企业微信机器人推送

0、前置条件 已经申请到企业微信机器人webhook&#xff0c;参考链接https://developer.work.weixin.qq.com/document/path/91770 1、创建报警媒介类型 在报警媒介类型右上角创建媒体类型 新增Token参数&#xff0c;将申请获得的Token填入 在脚本处填入脚本&#xff1a; 脚…

amtlib.dll打不开怎么办?一键修复丢失amtlib.dll方法

电脑丢失amtlib.dll文件是什么情况&#xff1f;出现amtlib.dll打不开怎么办&#xff1f;这样的情况有什么解决方法呢&#xff1f;今天就和大家聊聊amtlib.dll文件同时教大家一键修复丢失amtlib.dll方法&#xff1f;一起来看看amtlib.dll文件丢失会有哪些方法修复&#xff1f; a…

从旅游广告联想到《桃花源记》

近日收到《长江头条网》等知名网络自媒体相邀,促我写点儿旅游题材的文案。虽说笔者游历过许多名山大川的绝美风景区,但那是在70岁之前的事儿了。如今年逾78岁,纵使有少许自有资本能够支持出游,可体力难撑,岂不是花钱买罪受吗?而且,写没有亲身经历过的事挺难,即便发表出…

leetCode-hot100-数组专题之双指针

数组双指针专题 1.同向双指针1.1例题26.删除有序数组中的重复项27.移除元素80.删除有序数组中的重复项 Ⅱ 2.相向双指针2.1例题11.盛最多水的容器42.接雨水581.最短无序连续子数组 双指针在算法题中很常见&#xff0c;下面总结双指针在数组中的一些应用&#xff0c;主要分为两类…

WebGL的医学培训软件开发

开发基于WebGL的医学培训软件是一项复杂且技术性强的任务&#xff0c;需要结合医学专业知识和计算机图形学技术。以下是详细的开发流程和关键步骤。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1.需求分析与定义 目标用户&#xf…

二叉树——进阶(递归创建,非递归,广度优先,翻转,深度,对称)

二叉树——进阶 二叉树的递归创建非递归前中后序遍历非递归前序遍历非递归中序遍历非递归后序遍历 广度优先遍历二叉树&#xff08;层序遍历&#xff09;翻转二叉树 二叉树深度最大深度最小深度 对称二叉树 二叉树的递归创建 1&#xff0c;二叉树是一种结构相对固定的数据&…

vue3中element-plus下拉菜单与图标的使用

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 http://218.75.87.38:9666/ 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a; h…

用Python一键生成PNG图片的PowerPoint幻灯片

在当今的商业环境中,PowerPoint演示是展示和传递信息的常用方式。然而,手动将大量图像插入到幻灯片中往往是一项乏味且耗时的工作。但是,通过Python编程,我们可以轻松自动化这个过程,节省时间和精力。 C:\pythoncode\new\folderTOppt.py 在本文中,我将介绍如何使用Python、wx…

【C++初阶】vector

✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅ ✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨ &#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1…

在winnas中使用docker desktop遇到的问题及解决方法记录

最近在尝试从群晖转向winnas&#xff0c;一些简单的服务依然计划使用docker来部署。群晖的docker简单易用且稳定&#xff0c;在win上使用docker desktop过程中遇到了不少问题&#xff0c;在此记录一下以供后来人参考。 一、安装docker desktop后启动时遇到无法启动docker引擎 …

【Unity AR开发插件】四、制作热更数据-AR图片识别场景

专栏 本专栏将介绍如何使用这个支持热更的AR开发插件&#xff0c;快速地开发AR应用。 链接&#xff1a; Unity开发AR系列 插件简介 通过热更技术实现动态地加载AR场景&#xff0c;简化了AR开发流程&#xff0c;让用户可更多地关注Unity场景内容的制作。 “EnvInstaller…”支…

2024年电工杯数学建模竞赛A题完整解析 | 代码 论文分享

A 题 问题一1.1问题分析1.2第一问1.2.1指标定义1.2.2结果计算1.2.3关键因素分析 1.3第二问1.3.1模型建立1.3.2算法求解1.3.3求解结果 1.4第三问1.4.1模型建立1.4.2计算结果 第二题2.1 问题分析2.2第一问2.2.1指标计算 数据与代码代码分享完整资料 A题的问题一和问题二终于完成啦…

vue 纵向滚动菜单, 点击滚动到选中菜单

1 背景 需要设计一个纵向滚动菜单&#xff0c;要求丝滑点&#xff0c;默认显示选中菜单 2 思路 给定一个容器&#xff0c;样式包含overflow:hidden&#xff0c;默认高宽足够显示一个菜单&#xff08;以下用图标代替菜单&#xff09;&#xff0c;鼠标悬浮时增大容器高度&#…

电能抄表是什么?

1.电能抄表的概念和功能 电能抄表&#xff0c;说白了&#xff0c;是一种用于数据记录载入电力工程使用量的机器。它主要职能精确测量做好记录客户在一定时间内的耗电量&#xff0c;为供电公司提供准确的收费根据。电能抄表的应用&#xff0c;不仅方便了电费的清算&#xff0c;…

NSSCTF | [SWPUCTF 2021 新生赛]no_wakeup

打开题目后&#xff0c;点击三个&#xff1f;&#xff0c;发现是一个php序列化脚本 <?phpheader("Content-type:text/html;charsetutf-8"); error_reporting(0); show_source("class.php");class HaHaHa{public $admin;public $passwd;public function…

结构体变量的创建和初始化以及内存对齐

前言 嗨&#xff0c;我是firdawn&#xff0c;在本章中我们将介绍&#xff0c;结构体变量的创建和初始化&#xff0c;结构成员访问操作符以及结构体的内存对齐&#xff0c;下面是本章的思维导图&#xff0c;接下来&#xff0c;让我们开始今天的学习吧&#xff01; 一&#xf…

电脑远程控制另一台电脑怎么弄?

可以远程控制另一台电脑吗&#xff1f; “你好&#xff0c;我对远程访问技术不太了解。现在&#xff0c;我希望我的朋友可以远程控制我的Windows 10电脑&#xff0c;以便她能帮我解决一些问题。请问&#xff0c;有没有免费的方法可以实现这种远程控制&#xff1f;我该如何操作…

继“三级淋巴结”之后,再看看“单细胞”如何与AI结合【医学AI|顶刊速递|05-25】

小罗碎碎念 24-05-25文献速递 今天想和大家分享的是肿瘤治疗领域的另一个热点——单细胞技术&#xff0c;我们一起来看看&#xff0c;最新出炉的顶刊&#xff0c;是如何把AI与单细胞结合起来的。 另外&#xff0c;今天是周末&#xff0c;所以会有两篇文章——一篇文献速递&…