【数据结构】【C语言】堆~动画超详细解读!

目录

    • 1 什么是堆
      • 1.1 堆的逻辑结构和物理结构
      • 1.2 堆的访问
      • 1.3 堆为什么物理结构上要用数组?
      • 1.4 堆数据上的特点
    • 2 堆的实现
      • 2.1 堆类型定义
      • 2.2 需要实现的接口
      • 2.3 初始化堆
      • 2.4 销毁堆
      • 2.5 堆判空
      • 2.6 交换函数
      • 2.7 向上调整(小堆)
      • 2.8 向下调整(小堆)
      • 2.9 堆插入
      • 2.10 堆删除
      • 2.11 //堆顶
    • 3 完整代码
      • 3.1 heap.h
      • 3.2 heap.c

1 什么是堆

  • 简单来说堆是二叉树的一种表示方式,它在逻辑上就是一颗完全二叉树,它在物理上却是一个数组,这么说可能有点抽象,我们原来学习的栈,队列,或者说顺序表,链表等等,他们的逻辑结构和物理结构是相同或者相似的,就会比较好理解一些,而在堆这里物理结构和逻辑结构截然不同,理解相对就会比较抽象一些,我们接着看

1.1 堆的逻辑结构和物理结构

  • 逻辑结构即我们想象的结构,就比方说我们早上在图书馆排队的时候,放个包在图书馆门口,人可能都不见了,这个时候我们逻辑上认为我们在排队,但物理上我们同学就可能在吃早饭上厕所啥的
  • 逻辑上我们想象这个数组是一个二叉树,并且像二叉树一样访问子节点或者父节点
  • 比方说我给出以下数组,它在逻辑上是这样表示的(当然哈,指针其实是不存在的,只是逻辑上我们看作其是父子关系):
    请添加图片描述

1.2 堆的访问

  • 既然堆是一颗货真价实的二叉树,可我们怎么像二叉树一样,通过父/子节点访问子/父节点呢?

在这里插入图片描述

  1. 通过父节点访问子节点:
    • 我们假设父节点的下标为3,我们想访问它的子节点,只需要把 父节点的下标 * 2 + 1父节点的下标 * 2 + 2 即可 即 7 或 8
  2. 通过子节点访问父节点
    • 我们假设子节点的下标为7,我们想访问它的父节点,只需要把 (子节点的下标 - 1) / 2 即可 即 3
    • 我们假设子节点的下表为8,我们想访问它的父节点,依旧只需要把 (子节点的下标 - 1) / 2 即可 依旧是 3

1.3 堆为什么物理结构上要用数组?

  • 事实上学习堆是为了学习堆排序打基础,在堆排序中,有时候需要频繁交换头尾节点,如果用数组,找节点就会方便很多,交换函数也很好写,效率会更高,用链表要不断去遍历,或者专门写个尾指针妥协,很没必要
  • 其次,如果我们用链式存储的话,访问子/父节点需要定义3个指针,需要多开辟很多空间
  • 堆一定是完全二叉树,用数组存放会很方便,其中不会有空节点,所有数据存储都是连续的

1.4 堆数据上的特点

  • 堆必须要始终满足满足:父节点值比子节点小或者父节点始终比子节点大
  • 我们称父节点值始终比子节点小的堆为小堆/小根堆
  • 我们称父节点值始终比子节点大的堆为大堆/大根堆

例如:
1.大堆:
在这里插入图片描述

2.小堆:
在这里插入图片描述

2 堆的实现

2.1 堆类型定义

//堆在物理上是一个数组,我们直接按数组的定义方式就行
//类型定义
typedef int HPDataType;typedef struct Heap 
{HPDataType* data;int size;int capa;
}Heap;

2.2 需要实现的接口

//交换函数
void Swap(HPDataType* x, HPDataType* y);//向上调整(小堆)
void AdjustUp(HPDataType* data, int child);//向下调整(小堆)
void AdjustDown(HPDataType* data, int size, int father);//初始化堆
void HPInit(Heap* php);//销毁堆
void HPDestroy(Heap* php);//堆插入
void HPPush(Heap* php, HPDataType x);//堆删除
void HPPop(Heap* php);//堆顶
HPDataType HPTop(Heap* php);//堆判空
bool HPEmpty(Heap* php);

2.3 初始化堆

//像顺序表一样初始化就行
//初始化堆
void HPInit(HP* php)
{assert(php);php->a = (HPdatatype*)calloc(4, sizeof(HPdatatype));if (php->a == NULL){perror("HPInit::calloc fail");exit(1);}php->capa = 4;php->size = 0;
}

2.4 销毁堆

//同样,像顺序表一样销毁就行
//销毁堆
void HPdestory(HP* php)
{assert(php);free(php->a);php->a = NULL;php->capa = 0;php->size = 0;
}

2.5 堆判空

//堆判空
bool HPEmpty(HP* php)
{assert(php);   //判空return php->size == 0;    //size是0就返回true
}

2.6 交换函数

//只是完成数据在空间上的交换
//交换函数
void Swap(HPdatatype* x, HPdatatype* y)
{HPdatatype tmp = *x;*x = *y;*y = tmp;
}

2.7 向上调整(小堆)

  • 注意这里是重点
  • 向上调整会在很多地方用到,基本思想就是让本应该在上面的节点往上挪
//向上调整(小堆)
void AdjustUp(HPdatatype* a, int child)
{assert(a);//判空int father = (child - 1) / 2;//推算出父节点的位置while (father < child && a[father] > a[child])      //只要子节点比父节点还小,就让子节点和父节点交换{								               		//重复此步骤直到子节点大于父节点或者子节点和自己比较Swap(&a[child], &a[father]); child = father;father = (child - 1) / 2;}
}

请添加图片描述

2.8 向下调整(小堆)

  • 注意这里是重点
  • 向下调整同样会在很多地方用到
//向下调整(小堆)
void AdjustDown(HPdatatype* a, int size, int father)
{assert(a);//判空int child = (father * 2) + 1;      //先假设比较小的是左子节点if (child + 1 < size && a[child] > a[child + 1])        //如果右子节点比左子节点大,注意要判断一下子节点是否会超范围{child++;        //把child改成右子节点}while (child < size && a[father] > a[child] )    //如果父节点一直比子节点大就不断交换下移{                                                //直到子节点超出size范围或者父节点比子节点小就停下Swap(&a[father], &a[child]);     //交换father = child;              //重新找到父节点(交换后的父节点应该是原来的子节点的位置)child = (father * 2) + 1;    //重新定位子节点if (child + 1 < size && a[child] > a[child + 1])    //如法炮制{child++;}}
}

请添加图片描述

2.9 堆插入

  • 堆插入一般插入到末尾,因为末尾很空,啥也没有,比较好插入,插在其他地方还得先挪动才可以插入
//堆插入
void HPPush(HP* php, HPdatatype x)
{assert(php);   //判空//堆扩容,这里像数组一样扩容就行if (php->capa == php->size){HPdatatype* tmp = (HPdatatype*)realloc(php->a, 2 * php->size * sizeof(HPdatatype));if (tmp == NULL){perror("HPPush::realloc fail");exit(1);}php->a = tmp;php->capa *= 2;}php->a[php->size] = x;    //将要插入的数据放到堆低AdjustUp(php->a, php->size);      //通过向上调整找到这个数据本应该在的位置php->size++;      //别忘了让size++
}

2.10 堆删除

  • 堆删除一般删除堆顶的数据,但不能简单地把堆顶置为空,而是要和末尾数据交换,再一点点下调
//堆删除
void HPPop(HP* php)
{assert(php);     //判空if (!HPEmpty(php))    //如果堆不是空堆{Swap(&php->a[0], &php->a[php->size - 1]);   //交换堆顶和末尾的数据AdjustDown(php->a, php->size - 1, 0);    //将堆顶的数据向下挪到合适的位置php->size--;   //别忘了size--}
}

2.11 //堆顶

//取堆顶
HPdatatype HPTop(HP* php)
{assert(php);    //判空return HPEmpty(php) ? -1 : php->a[0];   //返回堆顶数据
}

  • 完整代码在最下面哦

佬!都看到这了,如果觉得有帮助的话一定要点赞啊佬 >v< !!!
放个卡密在这,感谢各位能看到这儿啦!
请添加图片描述


3 完整代码

3.1 heap.h

#pragma once//头文件声明
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
#include <string.h>//类型定义
typedef int HPdatatype;typedef struct Heap
{HPdatatype* a;int size;int capa;
}HP;//函数申明//交换函数
void Swap(HPDataType* x, HPDataType* y);//向上调整(小堆)
void AdjustUp(HPDataType* data, int child);//向下调整(小堆)
void AdjustDown(HPDataType* data, int size, int father);//初始化堆
void HPInit(Heap* php);//销毁堆
void HPDestroy(Heap* php);//堆插入
void HPPush(Heap* php, HPDataType x);//堆删除
void HPPop(Heap* php);//堆顶
HPDataType HPTop(Heap* php);//堆判空
bool HPEmpty(Heap* php);

3.2 heap.c

#include "heap.h"//初始化堆
void HPInit(HP* php)
{assert(php);php->a = (HPdatatype*)calloc(4, sizeof(HPdatatype));if (php->a == NULL){perror("HPInit::calloc fail");exit(1);}php->capa = 4;php->size = 0;
}//销毁堆
void HPdestory(HP* php)
{assert(php);free(php->a);php->a = NULL;php->capa = 0;php->size = 0;
}//堆判空
bool HPEmpty(HP* php)
{assert(php);return php->size == 0;
}//取堆顶
HPdatatype HPTop(HP* php)
{assert(php);return HPEmpty(php) ? -1 : php->a[0];
}//交换
void Swap(HPdatatype* x, HPdatatype* y)
{HPdatatype tmp = *x;*x = *y;*y = tmp;
}//向上调整(小堆)
void AdjustUp(HPdatatype* a, int child)
{assert(a);int father = (child - 1) / 2;while (father < child && a[father] > a[child]){Swap(&a[child], &a[father]);child = father;father = (child - 1) / 2;}
}//堆插入
void HPPush(HP* php, HPdatatype x)
{assert(php);//堆扩容if (php->capa == php->size){HPdatatype* tmp = (HPdatatype*)realloc(php->a, 2 * php->size * sizeof(HPdatatype));if (tmp == NULL){perror("HPPush::realloc fail");exit(1);}php->a = tmp;php->capa *= 2;}php->a[php->size] = x;AdjustUp(php->a, php->size);php->size++;
}//向下调整(小堆)
void AdjustDown(HPdatatype* a, int size, int father)
{assert(a);int child = (father * 2) + 1;if (child + 1 < size && a[child] > a[child + 1]){child++;}while (child < size && a[father] > a[child] ){Swap(&a[father], &a[child]);father = child;child = (father * 2) + 1;if (child + 1 < size && a[child] > a[child + 1]){child++;}}
}//堆删除
void HPPop(HP* php)
{assert(php);if (!HPEmpty(php)){Swap(&php->a[0], &php->a[php->size - 1]);AdjustDown(php->a, php->size - 1, 0);php->size--;}
}

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

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

相关文章

微服务项目收获和总结---第2,3天(分库分表思想,文章业务)

①分库分表思想 文章表一对一为什么要拆分&#xff1f;因为文章的内容会非常大&#xff0c;查询效率会很低&#xff0c;我们经常操作文章的基本信息&#xff0c;不会很经常查询文章内容。充分发挥高频数据的操作效率。 ②freemarker和minIO 由于文章内容数据量过大&#xff0c…

办公自动化-Python如何提取Word标题并保存到Excel中?

办公自动化-Python如何提取Word标题并保存到Excel中&#xff1f; 应用场景需求分析实现思路实现过程安装依赖库打开需求文件获取word中所有标题去除不需要的标题创建工作簿和工作表分割标题功能名称存入测试对象GN-TC需求标识符存入测试项标识存入需求标识符 完整源码实现效果学…

Vue02-黑马程序员学习笔记

一、今日学习目标 1.指令补充 指令修饰符v-bind对样式增强的操作v-model应用于其他表单元素 2.computed计算属性 基础语法计算属性vs方法计算属性的完整写法成绩案例 3.watch侦听器 基础写法完整写法 4.综合案例 &#xff08;演示&#xff09; 渲染 / 删除 / 修改数量 …

一个简约高级视差效果PR动态图文开场视频模板

这是一个高质量且易于定制的pr模板。具有模块化结构&#xff0c;可以轻松更改内容。包括视频教程&#xff0c;即使是新手小白也可以轻松套用模板制作视频。 主要特点&#xff1a; 水平&#xff08;19201080&#xff09;和垂直&#xff08;10801920&#xff09;分辨率&#xff…

c语言:利用随机函数产生20个[120, 834] 之间互不相等的随机数, 并利用选择排序法将其从小到大排序后输出(每行输出5个)

利用随机函数产生20个[120, 834] 之间互不相等的随机数&#xff0c; 并利用选择排序法将其从小到大排序后输出&#xff08;每行输出5个&#xff09; 代码如下&#xff1a; #include <stdio.h> #include <time.h> #include <stdlib.h> int shenchen(int a[…

三维模型相互转换(obj文件转inp文件)

三维模型文件根据其含义都是可以进行相互转换的&#xff0c;这里主要介绍obj文件转化为inp文件。 什么是inp文件&#xff1f; inp文件是以.inp为后缀的文本文件&#xff0c;它包括了模型的全部数据信息&#xff0c;ABAQUS求解器分析的对象是inp文件&#xff0c;软件生成的.ca…

AI智能体|扣子Coze“图像流”功能速览

大家好&#xff0c;我是无界生长。 AI智能体&#xff5c;扣子Coze“图像流”功能速览Coze提供易上手的图像处理工作流&#xff0c;包含智能生成、智能编辑和基础编辑三类节点&#xff0c;旨在通过AI技术简化图像处理过程。本文对扣子Coze“图像流”功能做了简单介绍&#xff0…

【qt】初识模型和视图

模型和视图 一.模型和视图的概念1.关系2.模型3.数据4.视图5.特点 二.文件系统模型1.那种数据&#xff1f;2.界面拖放3.创建模型4.模型设置数据5.视图设置模型6.模型索引7.模型操作数据①文件名②文件大小③文件类型④是否是目录⑤文件路径 三.字符串链表模型1.那种数据&#xf…

论Promise在前端江湖的地位及作用

系列文章&#xff1a; 先撸清楚&#xff1a;并发/并行、单线程/多线程、同步/异步 论Promise在前端江湖的地位及作用 前言 上篇文章阐述了并发/并行、单线程/多线程、同步/异步等概念&#xff0c;这篇将会分析Promise的江湖地位。 通过本篇文章&#xff0c;你将了解到&#x…

AI崛起,掌握它,开启智能新生活!

AI崛起&#xff0c;掌握它&#xff0c;开启智能新生活&#xff01; &#x1f604;生命不息&#xff0c;写作不止 &#x1f525; 继续踏上学习之路&#xff0c;学之分享笔记 &#x1f44a; 总有一天我也能像各位大佬一样 &#x1f3c6; 博客首页 怒放吧德德 To记录领地 &…

Linux中vim的基本使用

目录 vim中的三种模式以及基本操作命令模式(默认模式)插入模式底行模式 命令模式下的命令底行模式下的命令 vim是Linux和Unix环境下最基本的文本编辑器&#xff0c;类似于windows上的记事本 vim和Visual studio相比&#xff0c;vim并不集成&#xff0c;vim只能用来写代码 VS把写…

2024年5月24日 十二生肖 今日运势

小运播报&#xff1a;2024年5月24日&#xff0c;星期五&#xff0c;农历四月十七 &#xff08;甲辰年己巳月戊子日&#xff09;&#xff0c;法定工作日。 红榜生肖&#xff1a;龙、牛、猴 需要注意&#xff1a;兔、羊、马 喜神方位&#xff1a;东南方 财神方位&#xff1a;…

深度学习之基于Matlab的BP神经网络交通标志识别

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景与意义 随着智能交通系统&#xff08;ITS&#xff09;的快速发展&#xff0c;交通标志识别&#xff0…

BUUCTF---misc---[MRCTF2020]ezmisc

1、附件下载后是一张图片 2、查看属性&#xff0c;winhex分析&#xff0c;没发现什么 3、在kali中binwalk和foremost也没找到什么信息 4、用stegsolve分析也没发现什么 5、这里几乎常见的misc方法都试过了&#xff0c;还是没有发现什么 6、回归到图片本身&#xff0c;想到的…

基于51单片机智能大棚浇花花盆浇水灌溉补光散热设计

一.硬件方案 本设计通过光敏电阻检测光照强度&#xff0c;然后A/D模块PCF8591处理后&#xff0c;将光照强度值实时显示在液晶上&#xff0c;并且可以按键控制光照的强度值&#xff0c;当光照低于设定的阈值&#xff0c;1颗白色高亮LED灯亮进行补光&#xff0c;光照高于设定的阈…

第六节 自动装配源码理解

tips&#xff1a;不同版本代码实现有差异。 前面两章了解的流程&#xff0c;就是 SpringBoot 自动转配的核心。 一、自动装配 1.1 什么是 SpringBoot 自动装配? 自动装配是 Spring 框架用来减少配置的显式需求而引入的一个特性&#xff0c;该特性通过 Autowired或者Resource…

Redis数据库知识点

Redis set get del keys redis中有哪些数据类型 string 最大512m key层级 redis的key允许有多个单词形成层级结构&#xff0c;多个单词之间用‘:’隔开 set get del keys hash 本身在redis中存储方式就为key-value, 而hash数据结构中value又是一对key-value hset key …

【easyx】快速入门——弹球小游戏(第一代)

目录 1.需求 2.运动的小球 3.碰到边缘反弹 4.圆周撞击或越过边界反弹 5.绘制和移动挡板 6.小球碰到挡板反弹 7.游戏失败时该如何处理 8.随机初始条件 9.完整代码 我们这一节将结合动画和键盘交互的知识来做一个小游戏 1.需求 我们先看需求:小球在窗体内运动,撞到除…

后端数据库开发JDBC编程Mybatis之用基于XML文件的方式映射SQL语句实操

之前的SQL语句是基于注解 以后开发中一般是一个接口对应一个映射文件 书写映射文件 基本结构 框架 <?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.or…

尽在掌握:Android 13 通知新功能详解

尽在掌握&#xff1a;Android 13 通知新功能详解 在移动应用开发中&#xff0c;通知扮演着至关重要的角色&#xff0c;它如同应用程序与用户之间的桥梁&#xff0c;及时传递关键信息&#xff0c;提升用户体验。Android 13 作为最新的安卓版本&#xff0c;在通知方面带来了诸多…