数据结构——堆的实现(详解)

呀哈喽,我是结衣。

堆的介绍

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

性质

堆中某个节点的值总是不大于或不小于其父节点的值;
堆总是一棵完全二叉树。
在这里插入图片描述
大小堆如同所示。

堆的实现

介绍的话就到此为止,下面我们来进行堆的实现。无非就是那几样。

结构体的创建

typedef int HpDataType;
typedef struct heap
{HpDataType* a;int size;int capacity;
}HP;

是不是很熟悉,没错就是顺序表,但实现起来会比顺序表更难一些哦。

函数的声明

我们先把初始化,销毁,插入,删除等等要实现的函数声明一下:

void HpInit(HP* php);
void Hppush(HP* php, HpDataType x);
void Hpdestroy(HP* php);
void Hppop(HP* php);
bool Hpempty(HP* php);
HpDataType Hptop(HP* php);
int Hpsize(HP* php);

好像没多少函数?

初始化

和顺序表相同。

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

销毁

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

也是一样的说。

插入

这里开始就不一样了,我们要想怎么插才能维持小堆的结构。这里的插入是尾插,是直接插进去就不管了吗?
在这里插入图片描述
如果我们在最后插入的数据是一个大于等于56的数就没有问题,但是如果我们插入的是一个小于56的数呢?直接插入就不会满足小堆的结构了。
在这里插入图片描述
这里我们插入40,40比56小那么它应该是“父亲”,所以我们就要把他和56调换位置,但是如果我们再多想一想,我插入的是9呢,是不是就还要和10调换一下位置了?对的,这就是小堆的内涵,小的永远再上面。下面我们来写一下代码:

void Hppush(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");return;}php->a = tmp;php->capacity = newcapacity;}php->a[php->size++] = x;adjustup(php->a, php->size - 1);
}

中间有个扩容,我们讲过很多次了,就不展开讲了。
我们发现下面我有写了有个函数,向上调整的函数adjustup,没错上面我们讲的谁小于谁交换就要通过这个函数来实现。

向上调整

我们把size-1作为孩子传归来,这个还有一个重要的知识就是**父下标是子下标-1后除以2的结果。**这个用到是整形会自动忽略小数的机制。至于公式怎么来的,我把下标标出来了,你自己推推看就了解了。
在这里插入图片描述

void adjustup(HpDataType* a, int child)
{int parent = (child - 1) / 2;while (child>0){if (a[child] < a[parent]){swap(a,child, parent);child = parent;parent = (child - 1) / 2;}else{break;}}
}

里面还有一个swap函数,就是交换函数,它的实现在下面。回到adjustup,里面的判断大家应该是没有问题的吧,子与父交换后,我们就让child来到了parent的位置了,但是我们还要继续向上判断,所以parent改变为新child的父下标。
在这里插入图片描述

swap函数

void swap(HpDataType*a,int child, int parent)
{HpDataType tmp = a[child];a[child] = a[parent];a[parent] = tmp;
}

交换就是了

删除

在堆里删除也是一大难点,首先你来猜我们是头删还是尾删?
3
2
1
答案是头删加尾删,单纯尾删在这里没有意义,但这个头删也要用到尾删,你想想如果我们是把头删除了,后面的数再向前覆盖的话,那效率低且不说,而且亲子关系全部乱了。所以绝对不是单纯的头删,我们要把头数据和尾数据交换然后尾删。删除后再向下调整。

void Hppop(HP* php)
{assert(php);assert(php->size > 0);//int child = php->size-1;//int parent = (child - 1) / 2;swap(php->a, php->size - 1, 0);php->size--;adjustdown(php->a,0,php->size);
}

向下调整

void adjustdown(HpDataType* a,int parent,int size)
{int child = parent * 2 + 1;if (a[child] > a[child + 1]){child++;}while (child<size){if (a[parent] > a[child]){swap(a, child, parent);parent = child;child = parent * 2 + 1;if (a[child] > a[child + 1]){child++;}}else{break;}}
}

向下调整和向上调整也没太大的区别,都是判断后再交换,你只要清楚循环结束的条件就可以了。最重要的还是判断孩子中较小的数据,这里我们用的是假设法,我们先假设左孩子为小的数据,然后我们在判断如果不对就让孩子++,变成右孩子。

判空

bool Hpempty(HP* php)
{assert(php);return php->a == NULL;
}

返回顶部数据

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

判断有效数据的个数

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

测试

在这里插入图片描述

代码

heap.h

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
typedef int HpDataType;
typedef struct heap
{HpDataType* a;int size;int capacity;
}HP;
void HpInit(HP* php);
void Hppush(HP* php, HpDataType x);
void Hpdestroy(HP* php);
void Hppop(HP* php);
bool Hpempty(HP* php);
HpDataType Hptop(HP* php);
int Hpsize(HP* php);

heap.c

#include "heap.h"
void HpInit(HP* php)
{assert(php);php->a = NULL;php->capacity = 0;php->size = 0;
}
void Hpdestroy(HP* php)
{assert(php);free(php->a);php->a = NULL;php->capacity = 0;php->size = 0;
}
void swap(HpDataType*a,int child, int parent)
{HpDataType tmp = a[child];a[child] = a[parent];a[parent] = tmp;
}void adjustup(HpDataType* a, int child)
{int parent = (child - 1) / 2;while (child>0){if (a[child] < a[parent]){swap(a,child, parent);child = parent;parent = (child - 1) / 2;}else{break;}}
}
void Hppush(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");return;}php->a = tmp;php->capacity = newcapacity;}php->a[php->size++] = x;adjustup(php->a, php->size - 1);
}void adjustdown(HpDataType* a,int parent,int size)
{int child = parent * 2 + 1;if (a[child] > a[child + 1]){child++;}while (child<size){if (a[parent] > a[child]){swap(a, child, parent);parent = child;child = parent * 2 + 1;if (a[child] > a[child + 1]){child++;}}else{break;}}
}void Hppop(HP* php)
{assert(php);assert(php->size > 0);//int child = php->size-1;//int parent = (child - 1) / 2;swap(php->a, php->size - 1, 0);php->size--;adjustdown(php->a,0,php->size);
}
bool Hpempty(HP* php)
{assert(php);return php->a == NULL;
}HpDataType Hptop(HP* php)
{assert(php);assert(php->size > 0);return php->a[0];
}int Hpsize(HP* php)
{assert(php);return php->size;
}

test.c

#include "heap.h"int main()
{HP hp;HpInit(&hp);HpDataType* arr[] = { 21,141,41,1,1,42,9 };int sz = sizeof(arr) / sizeof(arr[0]);for (int i = 0; i < sz; i++){Hppush(&hp, arr[i]);}for (int i = 0; i < hp.size; i++){printf("%d ", hp.a[i]);}printf("\n");printf("%d ", hp.a[Hptop(&hp)]);printf("\n");Hppop(&hp);Hppop(&hp);Hppop(&hp);Hppop(&hp);Hppop(&hp);Hppop(&hp);for (int i = 0; i < hp.size; i++){printf("%d ", hp.a[i]);}return 0;
}


在这里插入图片描述

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

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

相关文章

Windows系统管理之备份与恢复

本章目录&#xff1a; 一. 本章须知&#xff1a; 前置条件 需要创建一个新的磁盘 前置条件2 给新添加的磁盘分盘 二. 了解开启并学会使用Windows sever backup 如何使用备份与恢复“备份计划”“一次性备份”“恢复” 最后是用命令行“一次性备份命令 ”完成一次备份 话不多说 …

React中如何解决点击<Tree>节点前面三角区域不触发onClick事件

React中如何解决点击节点前面三角区域不触发onClick事件&#xff0c;如何区别‘左边’和‘右边’区域点击逻辑呢&#xff1f;&#xff08;Tree引用开源组件TDesign&#xff09; 只需要在onClick里面加限制一下就行&#xff1a; <TreeexpandMutexactivabletransitiondata{t…

从0开始学习JavaScript--JavaScript函数返回值

在JavaScript中&#xff0c;函数是一种强大的工具&#xff0c;不仅能够执行一系列操作&#xff0c;还可以返回值。理解函数返回值的概念对于编写清晰、灵活的代码至关重要。本文将深入探讨JavaScript函数返回值的各种方面&#xff0c;包括基本返回值、多返回值、异步函数的返回…

STM32_8(DMA)

一、DMA DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输&#xff0c;无须CPU干预&#xff0c;节省了CPU的资源12个独立可配置的通道&#xff1a; DMA1&#xff08;7个通道&#xff09;&#xff…

吉他初学者学习网站搭建系列(3)——如何实现吉他在线调音

文章目录 背景知识teoriapitchytone效果 背景知识 学过初中物理就会知道&#xff0c;声音是由空气振动产生的。振动产生波&#xff0c;所以声音就是不同振幅和频率的波构成的。振幅决定了声音的响度&#xff0c;频率决定了声音的音高。想更进一步了解的可以访问这个网页wavefo…

万字解析设计模式之责任链模式、状态模式

目录 一、责任链模式 1.1概述 1.2结构 1.3实现 1.4 优缺点 1.5应用场景 1.6源码解析 二、状态模式 2.1概述 2.2结构 2.3实现 2.4优缺点 2.5应用场景 三、责任链模式实验 任务描述 实现方式 编程要求 测试说明 四、状态模式实验 任务描述 实现方式 编程要…

Leetcode—55.跳跃游戏【中等】

2023每日刷题&#xff08;四十&#xff09; Leetcode—55.跳跃游戏 贪心法实现代码 #define MAX(a, b) ((a > b)? (a): (b))bool canJump(int* nums, int numsSize) {int k 0;for(int i 0; i < numsSize; i) {if(i > k) {return false;}k MAX(k, i nums[i]);}r…

Linux(7):Vim 程序编辑器

vi 基本上 vi 共分为三种模式&#xff0c;分别是【一般指令模式】、【编辑模式】与【指令列命令模式】。 这三种模式的作用分别是&#xff1a; 一般指令模式(command mode) 以 vi 打开一个文件就直接进入一般指令模式了(这是默认的模式&#xff0c;也简称为一般模式)。在这个模…

微软 Edge 浏览器目前无法支持 avif 格式

avif 格式在微软 Edge 浏览器中还是没有办法支持。 如果你希望能够查看 avif 格式&#xff0c;那么只能通过浏览器打开&#xff0c;然后浏览器将会把这个文件格式下载到本地。 avif 格式已经在其他的浏览器上得到了广泛的支持&#xff0c;目前不支持的可能就只有 Edge 浏览器。…

管理后台系统,springboot+redis+nginx+html+bootstrap

一个简易版的管理后台系统&#xff0c;前后端分离&#xff0c;可适用于小团队开发&#xff0c;支持二次开发。 后端主要技术springboot&#xff0c;他可以帮我们快速的搭建项目&#xff0c;并快速实现开发。 redis做缓存&#xff0c;保存登录状态和一些高频率查询的基础数据。…

浅谈建筑节能监管平台在高校能源管理中的实践与应用

安科瑞 华楠 摘要&#xff1a;以节约型校园建设示范工程———宁夏大学节能监管平台项目建设为例&#xff0c;对系统的总体构架、关键技术、管理软件功能进行了详细的介绍。同时针对项目建设、运行和管理过程中出现的一些问题&#xff0c;提出有针对性的解决措施&#xff0c;为…

.NET6 开发一个检查某些状态持续多长时间的类

📢欢迎点赞 :👍 收藏 ⭐留言 📝 如有错误敬请指正,赐人玫瑰,手留余香!📢本文作者:由webmote 原创📢作者格言:新的征程,我们面对的不仅仅是技术还有人心,人心不可测,海水不可量,唯有技术,才是深沉黑夜中的一座闪烁的灯塔 !序言 在代码的世界里,时常碰撞…

从0到0.01入门 Webpack| 007.精选 Webpack面试题

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

Spring Cloud 版本升级遇坑记:OpenFeignClient与Gateway的恩怨情仇

Spring Cloud 版本升级遇坑记&#xff1a;OpenFeignClient与Gateway的恩怨情仇 近日&#xff0c;在对项目中的 Spring Boot、Spring Cloud 以及 Spring Cloud Alibaba 进行版本升级时&#xff0c;遭遇了一个令人头疼的问题&#xff1a;Spring Cloud Gateway 在运行时一直卡住&a…

探索亚马逊云科技云存储服务的性能

文章作者&#xff1a;Libai 引言 随着企业越来越多地依赖云存储解决方案&#xff0c;确保存储性能的最佳状态变得至关重要。在本文中&#xff0c;我们将探讨在亚马逊云科技云存储服务上进行存储性能基准测试的重要性&#xff0c;以及如何帮助企业做出资源分配和优化的明智决策…

20231125硬盘电源线5线不能识别日立10T的硬盘的解决方法

20231125硬盘电源线5线不能识别日立10T的硬盘的解决方法 2023/11/25 23:00 缘起&#xff0c;在拼多多买了2片10TB的7200rpm的日立二手硬盘。 型号&#xff1a;日立 mar-2018 10T硬盘 接上电脑&#xff0c;硬盘感觉在转动了【正常上电了。】 但是X99主板&#xff0c;在WIN10下就…

BC76 [NOIP2008]ISBN号码

#include<stdio.h> int main() {char arr[13]; //存放13位的ISBNint i, j;scanf("%s",arr);int s 0;for(i0, j1; i<11; i){if(arr[i] ! -){s (arr[i]-0)*j; //将字符换成int累加&#xff1a;0162……29158j; //执行if的时候加&#xff0c;不执行不加…

Springmvc原理解析

1. DispatcherServlet springmvc的核心控制器&#xff0c;负责截获所有的请求&#xff0c;当截获请求后委托给HandlerMapping进行请求映射的解析工作&#xff0c;目的是找到哪一个Controller的方法可以处理该请求&#xff0c;找到后再交由给HandlerAdaptor去负责调用并返回Mod…

个人测试用例以及测试金句分享

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 ☁️测试必背金句 界面易用可双安&…

【Apache Doris】一键实现万表MySQL整库同步 | 快速体验

【Apache Doris】一键实现万表MySQL整库同步 | 快速体验&#xff09; 一、 环境信息1.1 硬件信息1.2 软件信息 二、 流程介绍三、 前提概要3.1 安装部署3.2 JAR包准备3.2.1 数据源3.2.2 目标源 3.3 脚本模版 四、快速体验五、常见问题5.1 Mysql通信异常5.2 MySQL无Key同步异常5…