堆的概念与实现

devtools/2024/9/19 11:11:45/ 标签: 数据结构, 算法

目录

一、堆的介绍

1.堆的概念

2.堆的性质:

3.堆的结构

 二、堆的实现

1.堆的定义

2.接口函数

三、堆的实现

 1.堆的初始化

2.堆的销毁

 3.获取堆顶数据

4.判断堆是否为空

5. 堆的插入

 向上调整算法(重点)

向下调整算法(重点)

6.删除堆顶元素

7.堆的大小

 四、完整代码

heap.h

heap.c 


 

一、堆的介绍

1.堆的概念


堆:如果有一个关键码的集合K={k0,k1,k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足ki<=k2i+1且ki<=k2i+2(或满足ki>=k2i+1且ki>=k2i+2),其中i=0,1,2,…,则称该集合为堆。
小堆:将根结点最小的堆叫做小堆,也叫最小堆或小根堆。
大堆:将根结点最大的堆叫做大堆,也叫最大堆或大根堆。

2.堆的性质:


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

3.堆的结构

 二、堆的实现

1.堆的定义

typedef int HPDataType;
typedef struct Heap
{HPDataType* a;int size;int capacity;
}HP;

注:这里实现的是数组堆 

2.接口函数

 

void HeapInit(HP* php);
void HeapDestroy(HP* php);
//插入x继续保持堆形态
void HeapPush(HP* php, HPDataType x);
//删除堆顶元素
void HeapPop(HP* php);
HeapTop(HP* php);
//返回堆顶元素
HPDataType HeapTop(HP* php);
//判空
bool HeapEmpty(HP* php);
//堆的大小
int HeapSize(HP* php);
//向下调整
void AdjustDown(HPDataType* a, int n, int parent);
//向上调整
void AdjustUp(HPDataType* a, int child);
void Swap(HPDataType* p1, HPDataType* p2);

三、堆的实现

 1.堆的初始化

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

2.堆的销毁

void HeapDestroy(HP* php)
{free(php->a);free(php);php = NULL;
}

 3.获取堆顶数据

//返回堆顶元素
HPDataType HeapTop(HP* php)
{assert(php);return php->a[0];
}

4.判断堆是否为空

bool HeapEmpty(HP* php)
{assert(php);return php->size == 0;
}

5. 堆的插入

插入的核心思路

① 先将元素插入到堆的末尾,即最后一个孩子之后。

② 插入之后如果堆的性质遭到破坏,就将新插入的节点顺着其的父亲往上调整到合适位置。直到调到符合堆的性质为止。

根据堆的性质,如果不满足大堆和小堆,出现子大于父或父大于子的情况,为了保证插入之后堆还是堆,我们就需要进行自下往上的调整。堆插入数据对其他节点没有影响,只是可能会影响从它到根节点路径上节点的关系。

 向上调整算法(重点)

void Swap(HPDataType* p1, HPDataType* p2)
{HPDataType* 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]){Swap(&a[child], &a[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}

 父亲的下标=(孩子的下标-1)/2,知道了这个,就很好操作了,最坏的情况就是要调到根的位置,即parent=child,这里之所以是chile>0,而不是parent<0,是因为当child=0时,parent依然等于0,不可能小于0的。

向下调整算法(重点)

 现在我们给出一个数组,逻辑上看作一棵完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆

 

向下调整算法的基本思想(小堆):
 1.从根结点处开始,选出左右孩子中值较小的孩子。
 2.让小的孩子与其父亲进行比较。
 若小的孩子比父亲还小,则该孩子与其父亲的位置进行交换。并将原来小的孩子的位置当成父亲继续向下进行调整,直到调整到叶子结点为止。
 若小的孩子比父亲大,则不需处理了,调整完成,整个树已经是小堆了。

 我们首先定义一个minChild,这个是左孩子和右孩子中较小的孩子,parent和它进行交换。

这里调整中有两个结束条件,第一个是父亲小于孩子就停止,第二个是调整到叶子节点了,也就是minChild>n就调整完毕。

void AdjustDown(HPDataType* a, int n, int parent)
{int minChild = parent * 2 + 1;while (minChild < n){if ((minChild+1)<n&&a[minChild] > a[minChild + 1]){minChild++;}if (a[parent] > a[minChild]){Swap(&a[parent], &a[minChild]);parent = minChild;minChild = parent * 2 + 1;}else{break;}}}

使用堆的向下调整算法,最坏的情况下(即一直需要交换结点),需要循环的次数为:h - 1次(h为树的高度)。而h = (N为树的总结点数)。所以堆的向下调整算法的时间复杂度为:O(logN) 。 

6.删除堆顶元素

//删除堆顶元素 -- 找次打或者次小
void HeapPop(HP* php)
{ assert(php);assert(!HeapEmpty(php));Swap(&(php->a[0]),&(php->a[php->size-1]) );php->size--;AdjustDown(php->a, php->size, 0);
}

直接删除堆顶元素,会破坏堆的结构,所以这里我们让堆顶元素和最后一个元素进行交换,然后size--,重新向下调整,构建堆。 

7.堆的大小

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


 四、完整代码

heap.h

#define  _CRT_SECURE_NO_WARNINGS
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>typedef int HPDataType;
typedef struct Heap
{HPDataType* a;int size;int capacity;
}HP;void HeapInit(HP* php);
void HeapDestroy(HP* php);
//插入x继续保持堆形态
void HeapPush(HP* php, HPDataType x);
//删除堆顶元素
void HeapPop(HP* php);
HeapTop(HP* php);
//返回堆顶元素
HPDataType HeapTop(HP* php);
//判空
bool HeapEmpty(HP* php);
//堆的大小
int HeapSize(HP* php);
//向下调整
void AdjustDown(HPDataType* a, int n, int parent);
//向上调整
void AdjustUp(HPDataType* a, int child);
void Swap(HPDataType* p1, HPDataType* p2);

heap.c 

#define  _CRT_SECURE_NO_WARNINGS
#define  _CRT_SECURE_NO_WARNINGS
#include "heap.h"void HeapPrint(HP* php)
{for (int i = 0; i < php->size; ++i){printf("%d ", php->a[i]);}printf("\n");
}void HeapInit(HP* php)
{assert(php);php->a = NULL;php->size = php->capacity = 0;
}
void HeapDestroy(HP* php)
{free(php->a);free(php);php = NULL;
}void Swap(HPDataType* p1, HPDataType* p2)
{HPDataType* 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]){Swap(&a[child], &a[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}void AdjustDown(HPDataType* a, int n, int parent)
{int minChild = parent * 2 + 1;while (minChild < n){if ((minChild+1)<n&&a[minChild] > a[minChild + 1]){minChild++;}if (a[parent] > a[minChild]){Swap(&a[parent], &a[minChild]);parent = minChild;minChild = parent * 2 + 1;}else{break;}}}//插入x继续保持堆形态
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++;//向上调整AdjustUp(php->a, php->size - 1);}
//删除堆顶元素 -- 找次打或者次小
void HeapPop(HP* php)
{ assert(php);assert(!HeapEmpty(php));Swap(&(php->a[0]),&(php->a[php->size-1]) );php->size--;AdjustDown(php->a, php->size, 0);
}
//返回堆顶元素
HPDataType HeapTop(HP* php)
{assert(php);return php->a[0];
}
bool HeapEmpty(HP* php)
{assert(php);return php->size == 0;
}
int HeapSize(HP* php)
{return php->size;
}


http://www.ppmy.cn/devtools/114119.html

相关文章

el-table多选,分页切换时,选中内容不变;清空多选的内容

el-table中添加:row-key“getRowKeys” 设置true【 :reserve-selection“true”】 :row-key"getRowKeys" <el-table-column type"selection" :reserve-selection"true" width"55" align"center" fixed"left" …

day14-单例设计模式动态代理

一、单例设计模式 单例设计模式作用&#xff1a;确保一个类只有一个对象。场景&#xff1a;计算机中的回收站、任务管理器、Java中的Runtime类等好处&#xff1a;在这些业务场景下&#xff0c;使用单例模式&#xff0c;可以避免浪费内存。 1.1 饿汉式 饿汉式(提前创建对象)把类…

828华为云征文|华为云Flexus X实例docker部署Rocket.Chat构建属于自己的团队通讯协作平台

828华为云征文&#xff5c;华为云Flexus X实例docker部署Rocket.Chat构建属于自己的团队通讯协作平台 华为云最近正在举办828 B2B企业节&#xff0c;Flexus X实例的促销力度非常大&#xff0c;特别适合那些对算力性能有高要求的小伙伴。如果你有自建MySQL、Redis、Nginx等服务…

闲鱼网页版开放,爬虫的难度指数级降低。

爬虫&#xff0c;可以说是程序员最基础的热手项目。 之前我也一直说阿里系的签名系统搞得太复杂&#xff0c;风控太高&#xff0c;很不利于正常的自动化工具开发&#xff0c;这对于需要阿里应用的客户来说&#xff0c;也是一个很难覆盖的成本支出不是。 当然&#xff0c;我做项…

iPhone 16系列:摄影艺术的全新演绎,探索影像新境界

在科技的浪潮中&#xff0c;智能手机摄影功能的进化从未停歇。 苹果公司即将推出的iPhone 16系列&#xff0c;以其卓越的相机升级和创新特性&#xff0c;再次站在了手机摄影的前沿。 从硬件到软件&#xff0c;从拍照体验到图像处理&#xff0c;iPhone 16系列都展现了其在移动…

python毕业设计基于django+vue医院社区医疗挂号预约综合管理系统7918h-pycharm-flask

目录 技术栈和环境说明预期达到的目标具体实现截图系统设计Python技术介绍django框架介绍flask框架介绍解决的思路性能/安全/负载方面可行性分析论证python-flask核心代码部分展示python-django核心代码部分展示操作可行性技术路线感恩大学老师和同学详细视频演示源码获取 技术…

数据结构与算法-18算法专向(hash)

话题引入&#xff1a; 给你N&#xff08;1<N<10&#xff09;个自然数,每个数的范围为&#xff08;1~10000000000&#xff09;。现在让你以最快的速度判断某一个数是否在这N个数内&#xff0c;不得使用已经封装好的类&#xff0c;该如何实现。 A[] new int[N1]&#xff…

k8s1.27.7部署higress,代理非k8s集群业务

一、简介 Higress是基于阿里内部的Envoy Gateway实践沉淀、以开源Istio + Envoy为核心构建的云原生API网关,实现了流量网关 + 微服务网关 + 安全网关三合一的高集成能力,深度集成Dubbo、Nacos、Sentinel等微服务技术栈,能够帮助用户极大的降低网关的部署及运维成本且能力不…

使用llama.cpp 在推理MiniCPM-1.2B模型

llama.cpp 是一个开源项目&#xff0c;它允许用户在C中实现与LLaMA&#xff08;Large Language Model Meta AI&#xff09;模型的交互。LLaMA模型是由Meta Platforms开发的一种大型语言模型&#xff0c;虽然llama.cpp本身并不包含LLaMA模型的训练代码或模型权重&#xff0c;但它…

SQL数据库(MySQL)

一、在Ubuntu系统下安装MySQL数据库 1、更新软件源&#xff0c;在确保ubuntu系统能正常上网的情况下执行以下命令 sudo apt-get update 2、安装MySQL数据库及相关软件包 # 安装过程中设置root用户的密码 123456 sudo apt-get install mysql-server ​ # 安装访问数据库的客…

scanf()函数的介绍及基础用法

目录 scanf&#xff08;&#xff09;函数的介绍及基础用法 一&#xff1a;头文件 二&#xff1a;一般用法 三&#xff1a;返回值 1. 正整数的情况&#xff1a; 2. 0 的情况&#xff1a; 3. EOF的情况&#xff1a; 四&#xff1a;说明 scanf&#xff08;&#xff09;函数…

IP池对数据爬取工作的帮助

在数据爬取的过程中&#xff0c;IP池&#xff08;也称为代理IP池&#xff09;是一个极为重要的工具&#xff0c;它为数据抓取工作提供了多方面的支持和便利。本文将详细探讨IP池在数据爬取工作中的具体作用&#xff0c;以及它如何帮助提升数据抓取的效率、稳定性和合规性。 一…

新手教学系列——基于统一页面的管理后台设计(一)

在现代企业级应用中,后台管理系统往往是核心组成部分,特别是随着业务规模的扩展,如何在多个后端服务模块的基础上实现统一的登录验证、权限控制和页面管理,成为许多开发者面对的挑战。本文将以实际项目为例,详细讲解如何设计一个多模块的后台管理系统,满足不同服务模块的…

部署Prometheus+Grafana批量监控Linux服务器

在 Linux 服务器上使用 Docker 容器快速部署 Prometheus 和 Grafana 监控系统&#xff0c;同时通过 node_exporter 采集全面的系统性能数据。整个流程涵盖了从环境配置到搭建一个全面监控平台的每个步骤。 一键安装Node Exporter Node Exporter 是 Prometheus 生态系统中的一个…

上线跨境电商商城的步骤

上线一个跨境电商商城涉及多个步骤&#xff0c;从前期准备到上线后的维护。以下是一些关键步骤&#xff1a; 1. 市场调研与规划 目标市场分析&#xff1a;研究目标市场的需求、竞争对手和消费者行为。法律法规&#xff1a;了解并遵守目标市场的法律法规&#xff0c;包括税收、…

Unity携程Coroutine用法

一.携程概述 官方的解释是&#xff0c;携程允许你可以在多个帧中执行任务。在Unity中&#xff0c;携程是一个可以暂停并在后续帧中从暂停处继续执行的方法。 二.携程写法 下面示例使用携程和Update打印前5帧的时间间隔&#xff0c;展示了携程的基础写法 using System.Colle…

CentOS 入门必备基础知识

CentOS 是 Linux 发行版之一&#xff0c;基于 Red Hat Enterprise Linux&#xff08;RHEL&#xff09;&#xff0c;提供免费的企业级操作系统。对于初学者和系统管理员来说&#xff0c;了解 CentOS 的基础知识是必不可少的。本文将带你快速掌握 CentOS 的入门要点&#xff0c;帮…

vue-ts-demo

npm i -g vue/cli PS D:\kwai\vue3\project> vue create vue3-te-demo element-plus 一个 Vue 3 UI 框架 | Element Plus https://element-plus.org/zh-CN/guide/installation.html 安装&#xff1a; npm install element-plus --save 完整引入使用&#xff1a; 使用&…

STM32 芯片启动过程

目录 一、前言二、STM32 的启动模式三、STM32 启动文件分析1、栈 Stack2、堆 Heap3、中断向量表 Vectors3.1 中断响应流程 4、复位程序 Reset_Handler5、中断服务函数6、用户堆栈初始化 四、STM32 启动流程分析1、初始化 SP、PC 及中断向量表2、设置系统时钟3、初始化堆栈并进入…

判断变量是否为有限数字(非无穷大或NaN)math.isfinite() 判断变量是否为无穷大(正无穷大或负无穷大)math.isinf()

【小白从小学Python、C、Java】 【考研初试复试毕业设计】 【Python基础AI数据分析】 判断变量是否为有限数字&#xff08;非无穷大或NaN&#xff09; math.isfinite() 判断变量是否为无穷大&#xff08;正无穷大或负无穷大&#xff09; math.isinf() 请问关于以下代码表述错误…