数据结构第十一弹---堆

  • 1、堆的概念及结构
  • 2、堆的性质
  • 3、堆的调整算法
    • 3.1、向下调整算法
    • 3.2、向上调整算法
  • 4、堆的实现
    • 4.1、头文件包含和结构定义
    • 4.2、初始化
    • 4.3、销毁
    • 4.4、插入数据
    • 4.5、删除数据 删除堆顶
    • 4.6、获取堆顶元素
    • 4.7、获取有效数据个数
    • 4.8、判断是否为空
  • 5、代码汇总
    • 5.1、Heap.h
    • 5.2、Heap.c
  • 总结

1、堆的概念及结构

堆就是以二叉树的顺序存储方式来存储元素,同时又要满足父亲结点存储数据都要大于等于儿子结点存储数据(也可以是父亲结点数据都要小于等于儿子结点数据)的一种数据结构。堆只有两种即大堆和小堆大堆就是父亲结点数据大于等于儿子结点数据,小堆则反之。

在这里插入图片描述

2、堆的性质

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

3、堆的调整算法

3.1、向下调整算法

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

在这里插入图片描述
但是,使用向下调整算法需要满足一个前提:
 若想将其调整为小堆,那么根结点的左右子树必须都为小堆。
 若想将其调整为大堆,那么根结点的左右子树必须都为大堆。


在这里插入图片描述

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

代码实现

void Swap(HPDataType* a, HPDataType* b)
{HPDataType tmp = *a;*a = *b;*b = tmp;
}
void AdjustDown(HPDataType* a, int size, int parent)
{//1.假设左孩子为小的数据int child = parent * 2 + 1;while (child < size){//2.如果左孩子>右孩子 则将右孩子赋值//有可能只有左孩子 所以加条件//以下未有左右孩子且左孩子>右孩子情况,则将child++if (child + 1 < size && a[child] > a[child + 1]){child++;}//3.将孩子与父亲进行比较 如果孩子小则交换//然后将父亲和孩子移动到下一个位置if (a[child] < a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}

交换数值函数注意
在这里插入图片描述
使用堆的向下调整算法需要满足其根结点的左右子树均为大堆或是小堆才行,那么如何才能将一个任意树调整为堆呢?
 答案很简单,我们只需要从倒数第一个非叶子结点开始,从后往前,按下标,依次作为根去向下调整即可。
在这里插入图片描述

向下调整算法的时间复杂度:
在这里插入图片描述

根据计算可知,向下调整算法的时间复杂度为O(N)。

3.2、向上调整算法

当我们在一个堆的末尾插入一个数据后,需要对堆进行调整,使其仍然是一个堆,这时需要用到堆的向上调整算法。

在这里插入图片描述

向上调整算法的基本思想(小堆):
 1.将目标结点与其父结点比较。
 2.若目标结点的值比其父结点的值小,则交换目标结点与其父结点的位置,并将原目标结点的父结点当作新的目标结点继续进行向上调整。若目标结点的值比其父结点的值大,则停止向上调整,此时该树已经是小堆了。

在这里插入图片描述

但是,使用向上调整算法需要满足一个前提:
 若想将其调整为小堆,那么原来的数据为小堆。
 若想将其调整为大堆,那么原来的数据为大堆。


代码实现

void Swap(HPDataType* a, HPDataType* b)
{HPDataType tmp = *a;*a = *b;*b = tmp;
}
void AdjustUp(HPDataType* p, int child)
{int parent = (child - 1) / 2;while (child > 0){if (p[parent] > p[child]){Swap(&p[parent], &p[child]);child = parent;parent = (parent - 1) / 2;}else{break;}}
}

向上调整算法时间复杂度
在这里插入图片描述

因此向上调整算法的时间复杂度为O(N*logN)。

4、堆的实现

实现一个数据结构的第一步需要创建一个工程。(下图为vs 2022)
在这里插入图片描述
Heap.h(堆的类型定义、接口函数声明、引用的头文件)
Heap.c(堆接口函数的实现)
test.c (主函数、测试顺序表各个接口功能)

4.1、头文件包含和结构定义

以下是实现堆可能用到的头文件。

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

堆的结构定义

typedef int HPDataType;
typedef struct Heap
{HPDataType* a;//存放数据的动态数组int size;     //有效数据个数int capacity; //数组容量
}HP;

4.2、初始化

void HeapInit(HP* php)
{assert(php);//断言避免出现空指针php->a = NULL;php->capacity = php->size = 0;
}

4.3、销毁

void HeapDestory(HP* php)
{assert(php);free(php->a);//释放动态数组php->size = php->capacity = 0;
}

4.4、插入数据

插入数据的同时需要保证插入之后的数据依然是堆,由于插入数据之前的所有数据是堆,可以使用向上调整数据进行调整。
在这里插入图片描述

void HeapPush(HP* php, HPDataType x)
{assert(php);//1.检查容量if (php->size == php->capacity){int newCapacity = php->capacity == 0 ? 4 : 2 * php->capacity;HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType)*newCapacity);if (tmp == NULL){perror("realloc fail");exit(-1);}php->a = tmp;php->capacity = newCapacity;}//2.插入数据php->a[php->size] = x;php->size++;//3.调整数据AdjustUp(php->a, php->size-1);
}

测试
在这里插入图片描述

4.5、删除数据 删除堆顶

删除堆顶元素,首先需要有数据,通过断言判断,有数据的情况下先将堆顶元素和数组尾的数据进行交换,然后将size–,因为除了堆顶元素不满足堆结构之外,其他都满足,所以使用向下调整数据算法。

void HeapPop(HP* php)
{assert(php);//有数据才删除assert(php->size > 0);//1.将首位数据交换Swap(&php->a[0], &php->a[php->size - 1]);//2.删除尾数据php->size--;//3.向下调整AdjustDown(php->a, php->size, 0);
}

测试
在这里插入图片描述

4.6、获取堆顶元素

根据堆的定义可知,堆顶元素就是数组首元素,返回首元素即可。

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

测试
在这里插入图片描述

4.7、获取有效数据个数

根据堆的结构设计,size就是堆的有效数据个数,返回size即可。

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

测试
在这里插入图片描述

4.8、判断是否为空

根据堆结构的设计,size代表堆的有效数据个数,size等于0则为空,不等于0则不为空。

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

在这里插入图片描述

5、代码汇总

5.1、Heap.h

#pragma once     //防止头文件重复包含
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int HPDataType;
typedef struct Heap
{HPDataType* a;//存放数据的数组int size;     //有效数据个数int capacity; //数组容量
}HP;//初始化
void HeapInit(HP* php);
//销毁
void HeapDestory(HP* php);
//插入数据
void HeapPush(HP* php, HPDataType x);
//删除数据 删除堆顶数据
void HeapPop(HP* php);
//取堆顶元素
HPDataType HeapTop(HP* php);
//获取有效数据个数
size_t HeapSize(HP* php);
//判断是否为空
bool HeapEmpty(HP* php);
//两个元素交换
void Swap(HPDataType* a, HPDataType* b);//向上调整数据 小堆
void AdjustUp(HPDataType* p, int child);
//向下调整算法 小堆
void AdjustDown(HPDataType* a, int size, int parent);

5.2、Heap.c

#define _CRT_SECURE_NO_WARNINGS
#include "Heap.h"
//初始化 小堆
void HeapInit(HP* php)
{assert(php);php->a = NULL;php->capacity = php->size = 0;
}
//销毁
void HeapDestory(HP* php)
{assert(php);free(php->a);php->size = php->capacity = 0;
}
void Swap(HPDataType* a, HPDataType* b)
{HPDataType tmp = *a;*a = *b;*b = tmp;
}
log N
//向上调整数据 小堆
void AdjustUp(HPDataType* p, int child)
{int parent = (child - 1) / 2;while (child > 0){if (p[parent] > p[child]){Swap(&p[parent], &p[child]);child = parent;parent = (parent - 1) / 2;}else{break;}}
}//插入数据
void HeapPush(HP* php, HPDataType x)
{assert(php);//1.检查容量if (php->size == php->capacity){int newCapacity = php->capacity == 0 ? 4 : 2 * php->capacity;HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType)*newCapacity);if (tmp == NULL){perror("realloc fail");exit(-1);}php->a = tmp;php->capacity = newCapacity;}//2.插入数据php->a[php->size] = x;php->size++;//3.调整数据AdjustUp(php->a, php->size-1);
}//向下调整算法 小堆
void AdjustDown(HPDataType* a, int size, int parent)
{//1.假设左孩子为小的数据int child = parent * 2 + 1;while (child < size){//2.如果左孩子>右孩子 则将右孩子赋值//有可能只有左孩子 所以加条件//以下未有左右孩子且左孩子>右孩子情况,则将child++if (child + 1 < size && a[child] > a[child + 1]){child++;}//3.将孩子与父亲进行比较 如果孩子小则交换//然后将父亲和孩子移动到下一个位置if (a[child] < a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}//删除数据 删除堆顶数据
void HeapPop(HP* php)
{assert(php);//有数据才删除assert(php->size > 0);//1.将首位数据交换Swap(&php->a[0], &php->a[php->size - 1]);//2.删除尾数据php->size--;//3.向下调整AdjustDown(php->a, php->size, 0);
}
//取堆顶元素
HPDataType HeapTop(HP* php)
{assert(php);assert(php->size > 0);return php->a[0];
}
size_t HeapSize(HP* php)
{assert(php);return php->size;
}
bool HeapEmpty(HP* php)
{assert(php);return php->size == 0;
}

总结

本篇博客就结束啦,谢谢大家的观看,如果公主少年们有好的建议可以留言喔,谢谢大家啦!

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

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

相关文章

好用的设备租赁管理软件有哪些?

“我们公司是做设备租赁的&#xff0c;想找一款适合设备租赁的库存管理软件&#xff0c;最好有库存管理&#xff0c;客户信息&#xff0c;设备外调管理&#xff0c;租赁天数管理&#xff0c;设备的借出与归还信息管理与查询。” 总结一下—— 库存管理客户信息管理设备租赁管…

阿里云服务器 使用Certbot申请免费 HTTPS 证书及自动续期

前言 Certbot是一款免费且开源的自动化安全证书管理工具&#xff0c;由电子前沿基金会&#xff08;EFF&#xff09;开发和维护&#xff0c;是在Linux、Apache和Nginx服务器上配置和管理SSL/TLS证书的一种机制。Certbot可以自动完成域名的认证并安装证书。 一、 安装软件 1.1…

38.深入MySQL

深入MySQL 索引 索引是关系型数据库中用来提升查询性能最为重要的手段。关系型数据库中的索引就像一本书的目录&#xff0c;我们可以想象一下&#xff0c;如果要从一本书中找出某个知识点&#xff0c;但是这本书没有目录&#xff0c;这将是意见多么可怕的事情&#xff01;我们…

JVM如何执行synchronized修饰的方法

首先市面上关于synchronized的资料已经很多了&#xff0c;但是大家对于底层的原理实现可能没有什么概念&#xff0c;大多都是死记硬背&#xff0c;所以我想通过实战的方式给大家带来一些不一样的体验。具体体现在哪些方面呢&#xff1f; 更系统。市面上目前虽然资料众多&#x…

WPF 入门教程DispatcherTimer计时器

https://www.zhihu.com/tardis/bd/art/430630047?source_id1001 在 WinForms 中&#xff0c;有一个名为 Timer 的控件&#xff0c;它可以在给定的时间间隔内重复执行一个操作。WPF 也有这种可能性&#xff0c;但我们有DispatcherTimer控件&#xff0c;而不是不可见的控件。它几…

15-链表-环形链表 II

这是链表的第15题&#xff0c;力扣链接。 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链…

中科驭数诚邀您参加第二届证券基金行业先进计算技术大会暨2024低时延技术创新实践论坛(上海站)

低时延技术是证券基金期货领域业务系统的核心技术&#xff0c;是打造极速交易系统领先优势的关键&#xff0c;也是证券基金行业关注的前沿技术热点。 继去年圆满举办首届证券基金行业先进计算技术大会之后&#xff0c;在中国计算机学会集成电路设计专委、先进计算产业联盟、证…

在macos上查看当前进程的栈信息

概述 在调试程序时&#xff0c;如cpu莫名的高或低&#xff0c;一个常用的方式就是打印当前进行的调用栈&#xff0c;然后确认各线程的执行函数是否有异常。 在linux系统中可以使用pstack命令&#xff0c;直接打印各线程的栈信息&#xff0c;可惜在macos上没有该命令。一种解决…

25 心形按钮

效果演示 实现了一个心形的心形图案&#xff0c;当用户点击图案时&#xff0c;图案会旋转并缩小&#xff0c;同时背景颜色会变成白色。 Code <div class"love"><input id"switch" type"checkbox"><label class"love-heart&…

049.Python包和模块_虚拟环境超详细讲解

我 的 个 人 主 页&#xff1a;&#x1f449;&#x1f449; 失心疯的个人主页 &#x1f448;&#x1f448; 入 门 教 程 推 荐 &#xff1a;&#x1f449;&#x1f449; Python零基础入门教程合集 &#x1f448;&#x1f448; 虚 拟 环 境 搭 建 &#xff1a;&#x1f449;&…

二叉树的层序遍历经典问题(算法村第六关白银挑战)

基本的层序遍历与变换 二叉树的层序遍历 102. 二叉树的层序遍历 - 力扣&#xff08;LeetCode&#xff09; 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 示例 1&#xff1a; 输入…

Java顺序表(1)

&#x1f435;本篇文章将对顺序表中的方法进行模拟实现 一、线性表 线性表是指在逻辑结构上呈连续的线性结构&#xff0c;而在物理结构上不一定是连续的结构&#xff0c;常见的线性表有&#xff1a;顺序表、链表、栈、队列等 二、顺序表 顺序表一般采用数组来存储数据&#x…

【STM32】| 01——常用外设 | USART

系列文章目录 【STM32】| 01——常用外设 | USART 失败了也挺可爱&#xff0c;成功了就超帅。 文章目录 前言1. 基础理论1.1 并行通信和串行通信1.2 同步通信和异步通信1.3 单工/半双工/全双工1.4 电平信号(RS232/TTL)和差分信号(RS485)1.5 端口(COM) 2. 串口理论2.1 串口物理…

Springboot+RocketMQ通过事务消息优雅的实现订单支付功能

目录 1. 事务消息 1.1 RocketMQ事务消息的原理 1.2 RocketMQ订单支付功能设计 1. 事务消息 RocketMQ的事务消息&#xff0c;是指发送消息事件和其他事件需要同时成功或同时失败。比如银行转账&#xff0c; A银行的某账户要转一万元到B银行的某账户。A银行发送“B银行账户增加…

高级分布式系统-第3讲 网络与网络互联

万维网的诞生 1957年10月4日&#xff0c; 苏联发射了人类第一颗人造卫星—斯普特尼克一号 美国政府震惊不已。 他们认为&#xff0c; 在日趋激烈的冷战对抗中&#xff0c; 自己已经全面落后于苏联。 为了扭转这一局面&#xff0c; 美国国防部很快于1958 年 2 月组建了一个神秘…

Fastdata极数公司介绍

【写在开头】 前不久看到一句话和一个新闻&#xff0c;“数据作为主要生产要素&#xff0c;以资源化为起点&#xff0c;经历资产化、资本化阶段&#xff0c;逐步实现数据价值。” 2023年10月25日&#xff0c;国家数据局正式揭牌&#xff0c;由国家发展和改革委员会管理。 初看…

linux安装node.js

先去官网下载对应的版本 官网&#xff1a;https://nodejs.org/en/download 选择对应的版本&#xff0c;点这个&#xff0c;直接去虚拟机上面安装 # apt的系统 apt install -y wget # yum的系统 yum install -y wget下载包 wget https://nodejs.org/dist/v20.10.0/node-v20.…

【小白专用】(C#)用户、角色、权限控制体系

我们在开发很多项目的时候,都会用到用户权限管理,我也在很多项目里做过权限控制,所以,我也总结出一套条理清晰的角色权限控制体系。本文采用RBAC&#xff08;Role Based Access Control&#xff09;的基本思想&#xff0c;RBAC&#xff08;角色访问控制&#xff09;的基本思想可…

Java可视化大屏智慧工地云平台源码(SaaS模式)

智慧工地是一种崭新的工程现场一体化管理模式&#xff0c;是互联网与传统建筑行业的深度融合。它充分利用移动互联、物联网、云计算、大数据等新一代信息技术&#xff0c;围绕人、机、料、法、环等各方面关键因素&#xff0c;彻底改变传统建筑施工现场参建各方现场管理的交互方…

5G前装搭载率即将迈过10%大关,车载通讯进入多层次增长通道

对于智能化来说&#xff0c;车载通讯性能的提升&#xff0c;对于相关功能的用户体验优化、进一步减少通讯时延以及打开应用新空间&#xff0c;至关重要。 目前&#xff0c;2G/3G正在进入运营商逐步关闭运营的阶段&#xff0c;4G依然是主力&#xff0c;但5G也在迎来新的增长机会…