【数据结构与算法】之堆及其实现!

目录

1、堆的概念及结构

2、堆的实现 

2.1 堆向下和向上调整算法

2.2 堆的创建

2.3 建堆时间复杂度

2.4 堆的插入

2.5 堆的删除

2.6 完整代码

3、完结散花


                                                                                个人主页:秋风起,再归来~

                                                                                            数据结构与算法                             

                                                                       个人格言:悟已往之不谏,知来者犹可追

                                                                                        克心守己,律己则安!

1、堆的概念及结构

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

堆的性质:

堆中某个节点的值总是不大于或不小于其父节点的值;

堆总是一棵完全二叉树。

2、堆的实现 

2.1 堆向下和向上调整算法

现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整 成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

int array[] = {27,15,19,18,28,34,65,49,25,37};

代码实现:

1、向上调整算法:

//交换
void swap(HPDataType* p1, HPDataType* p2)
{HPDataType tmp = *p1;*p1 = *p2;*p2 = tmp;
}//向下调整
void AdjustDown(HPDataType* a, int size, int parent)
{//先假设做孩子小int child = parent * 2 + 1;while (child < size)//当孩子超过最后一个叶子时结束循环{if ((child + 1 < size) && a[child + 1] < a[child])//如果右孩子小于左孩子,右孩子与父亲比较{child++;}if (a[child] < a[parent])//建小堆,即小的当父亲{swap(&a[child] , &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}

2、向下调整算法

//向下调整
void AdjustDown(HPDataType* a, int size, int parent)
{//先假设做孩子小int child = parent * 2 + 1;while (child < size)//当孩子超过最后一个叶子时结束循环{if ((child + 1 < size) && a[child + 1] < a[child])//如果右孩子小于左孩子,右孩子与父亲比较{child++;}if (a[child] < a[parent])//建小堆,即小的当父亲{swap(&a[child] , &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}

2.2 堆的创建

下面我们给出一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算 法,把它构建成一个堆。根节点左右子树不是堆,我们怎么调整呢?这里我们从倒数的第一个非叶子节点的 子树开始调整,一直调整到根节点的树,就可以调整成堆。

int a[] = {1,5,3,8,7,6}; 

//建堆算法一(从第一个孩子向上调整)
//这种写法的时间复杂度为n*logn
//for (int i = 1; i < len; i++)
//{
//	AdjustUp(a, i);//从第一个孩子向上调整建堆
//}
// 
//建堆算法二(从倒数第一个根向下调整)
//这种写法的时间复杂度为O(N)[更优]
for (int i = (len - 1 - 1) / 2; i >= 0; i--)
{AdjustDown(a, len, i);//从倒数第一个根向下调整建堆
}

2.3 建堆时间复杂度

因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来看的 就是近似值,多几个节点不影响最终结果):

因此:向下调整建堆的时间复杂度为O(N)。 

2.4 堆的插入

先插入一个10到数组的尾上,再进行向上调整算法,直到满足堆。

// 堆的插入
void HeapPush(Heap* hp, HPDataType x)
{//先判断容量大小是否足够if (hp->_size == hp->_capacity){int newcapacity = hp->_capacity == 0 ? hp->_capacity = 4 : hp->_capacity * 2;//如果原来没有空间,就给上4,有的话就扩大为原来的两倍HPDataType* ptr = (HPDataType*)realloc(hp->_a, newcapacity * sizeof(HPDataType));//动态扩容if (ptr == NULL){perror("realloc fail;");return;}//也可以用assert断言一下hp->_a = ptr;//开辟成功将地址传给arrhp->_capacity = newcapacity;//更新容量}//对堆进行插入操作hp->_a[hp->_size] = x;hp->_size++;//插入一个数据后就进行向上调整,保证堆的结构AdjustUp( hp->_a,  hp->_size-1);
}

2.5 堆的删除

删除堆是删除堆顶的数据,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调 整算法。

// 堆的删除
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);
}

2.6 完整代码

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;
}Heap;
//堆的初始化
void HeapInit(Heap* hp);// 堆的销毁
void HeapDestory(Heap* hp);// 堆的插入
void HeapPush(Heap* hp, HPDataType x);// 堆的删除
void HeapPop(Heap* hp);// 取堆顶的数据
HPDataType HeapTop(Heap* hp);// 堆的数据个数
int HeapSize(Heap* hp);// 堆的判空
bool HeapEmpty(Heap* hp);

Heap.c 

#include"Heap.h"//堆的初始化
void HeapInit(Heap* hp)
{assert(hp);hp->_a = NULL;hp->_capacity = hp->_size = 0;
}// 堆的销毁
void HeapDestory(Heap* hp)
{assert(hp);free(hp->_a);hp->_a = NULL;hp->_capacity = hp->_size = 0;
}//交换
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 HeapPush(Heap* hp, HPDataType x)
{//先判断容量大小是否足够if (hp->_size == hp->_capacity){int newcapacity = hp->_capacity == 0 ? hp->_capacity = 4 : hp->_capacity * 2;//如果原来没有空间,就给上4,有的话就扩大为原来的两倍HPDataType* ptr = (HPDataType*)realloc(hp->_a, newcapacity * sizeof(HPDataType));//动态扩容if (ptr == NULL){perror("realloc fail;");return;}//也可以用assert断言一下hp->_a = ptr;//开辟成功将地址传给arrhp->_capacity = newcapacity;//更新容量}//对堆进行插入操作hp->_a[hp->_size] = x;hp->_size++;//插入一个数据后就进行向上调整,保证堆的结构AdjustUp( hp->_a,  hp->_size-1);
}//向下调整
void AdjustDown(HPDataType* a, int size, int parent)
{//先假设做孩子小int child = parent * 2 + 1;while (child < size)//当孩子超过最后一个叶子时结束循环{if ((child + 1 < size) && a[child + 1] < a[child])//如果右孩子小于左孩子,右孩子与父亲比较{child++;}if (a[child] < a[parent])//建小堆,即小的当父亲{swap(&a[child] , &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}// 堆的删除
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);
}// 取堆顶的数据
HPDataType HeapTop(Heap* hp)
{assert(hp);assert(hp->_size > 0);return hp->_a[0];
}// 堆的数据个数
int HeapSize(Heap* hp)
{assert(hp);return hp->_size;
}// 堆的判空
bool HeapEmpty(Heap* hp)
{assert(hp);return hp->_size == 0;
}

3、完结散花

好了,这期的分享到这里就结束了~

如果这篇博客对你有帮助的话,可以用你们的小手指点一个免费的赞并收藏起来哟~

如果期待博主下期内容的话,可以点点关注,避免找不到我了呢~

我们下期不见不散~~

​​​​

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

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

相关文章

Hadoop3:HDFS的Fsimage和Edits文件介绍

一、概念 Fsimage文件&#xff1a;HDFS文件系统元数据的一个永久性的检查点&#xff0c;其中包含HDFS文件系统的所有目 录和文件inode的序列化信息。 Edits文件&#xff1a;存放HDFS文件系统的所有更新操作的路径&#xff0c;文件系统客户端执行的所有写操作首先 会被记录到Ed…

【状态压缩dp】最短Hamilton路径

题意&#xff1a; 从0开始&#xff0c;必须走完全部节点&#xff0c;且不重复走&#xff0c;不漏走的最短距离 关键思路&#xff1a; 从0开始 走到j 节点所走情况是 state【state表示经过的点&#xff0c;不代表顺序&#xff0c;就表示经过的点】 f[i][j]表示 从0开始 走到j…

经纬恒润第三代重载自动驾驶平板车

随着无人驾驶在封闭场地和干线道路场景的加速落地&#xff0c;港口作为无人化运营的先行者&#xff0c;其场景的复杂度、特殊性对无人化运营的技术提出了各种挑战。经纬恒润作为无人驾驶解决方案提供商&#xff0c;见证了港口在无人化运营方面的尝试及发展&#xff0c;并深度参…

Python——基于共享单车使用量数据的可视化分析(1)

目录 &#x1f9fe; 1、数据集&#xff08;部分数据&#xff09; ✏️ 2、导入数据集与必要模块 1️⃣ 2.1 导入库以及字体包 2️⃣ 2.2 读取数据集 3️⃣ 2.3 查看数据集基本信息 ⌨️ 3、数据预处理 1️⃣ 3.1删除无关字段 2️⃣ 3.2对各字段进行中文标识 3️⃣ 3.3…

go mod模式下,import gitlab中的项目

背景 为了go项目能够尽可能复用代码&#xff0c;把一些公用的工具类&#xff0c;公用的方法等放到共用包里统一管理。把共用包放到gitlab的私有仓库中。 遇到的问题 通过https方式&#xff0c;执行go get报了错误。 通过ssh方式&#xff0c;执行go get报了错误。 修改配置&am…

Linux备份服务及rsync企业备份架构(应用场景)

备份服务概述 备份服务:需要使用到脚本,打包备份,定时任务. 备份服务:rsyncd服务,不同主机之间数据传输. 特点&#xff1a; rsync是个服务也是命令使用方便&#xff0c;具有多种模式传输数据的时候是增量传输 增量与全量&#xff1a; 全量 &#xff1a;无论多少数据全部推…

研发机构大数据迁移如何保障敏感数据不泄露

随着云计算和大数据技术的飞速进步&#xff0c;越来越多的企业正试图通过数据迁移来提升IT基础设施的效率&#xff0c;减少成本&#xff0c;并增强业务的灵活性。但是&#xff0c;这一过程并非没有它的挑战&#xff0c;尤其是在数据安全方面。数据在转移过程中可能会遭遇黑客攻…

光伏企业都在用的户用光伏管理软件——鹧鸪云

随着全球对可再生能源和清洁能源的需求日益增长&#xff0c;光伏产业作为其中的佼佼者&#xff0c;正迎来前所未有的发展机遇。然而&#xff0c;随着光伏电站规模的扩大和分布范围的增加&#xff0c;如何高效、智能地管理这些电站&#xff0c;确保它们稳定、安全地运行&#xf…

k8s遇到的错误记录

时隔四年有开始重新鼓捣k8s了&#xff0c;重新安装后遇到的错误记录如下&#xff1a; Error: Package: kubelet-1.14.0-0.x86_64 (kubernetes) Requires: kubernetes-cni 0.7.5 Available: kubernetes-cni-0.3.0.1-0.07a8a2.x86_64 (kubernetes) …

数美滑块研究

周一&#xff0c;在清晨的阳光照耀下&#xff0c;逆向山脚下的小镇宁静而安详。居民们忙碌地开始一天的生活&#xff0c;而在爬虫镇子的边缘&#xff0c;一座古朴的道观显得格外神秘。 阿羊正静静地坐在青石长凳上&#xff0c;摸鱼养神。突然&#xff0c;一道清脆的声音在他耳…

element-plus:踩坑日记

el-table Q&#xff1a;有fixed属性时&#xff0c;无数据时&#xff0c;可能出现底部边框消失的bug 现象&#xff1a; 解决方法&#xff1a; .el-table__empty-block {border-bottom: 1px solid var(--el-table-border-color); } el-collapse 折叠面板 Q&#xff1a;标题上…

为什么说 Redis 是单线程的?——Java全栈知识(25)

为什么说 Redis 是单线程的&#xff1f; 我们常说的 Redis 是单线程的&#xff0c;但是我前面在讲持久化机制的时候又说 RDB 的持久化是通过主进程 fork 出一个子进程来实现 RDB 持久化。那么 Redis 到底是多线程还是单线程的呢&#xff1f; Redis 的网络 IO 和键值的读写是单…

leetcode 1225 报告系统状态的连续日期(postgresql)

需求 系统 每天 运行一个任务。每个任务都独立于先前的任务。任务的状态可以是失败或是成功。 编写一个 SQL 查询 2019-01-01 到 2019-12-31 期间任务连续同状态 period_state 的起止日期&#xff08;start_date 和 end_date&#xff09;。即如果任务失败了&#xff0c;就是失…

Linux网络之策略路由

一、前言 日常维护工作中,有时候会遇到单台主机多张网卡的情况,尤其云上环境,云主机多张网卡是一种常见网络配置场景,那如何让多网卡正常工作呢,本期基于此北京,回顾下Linux策略路由的相关知识; 策略路由PBR:(Policy-Based-Route),也称为源路由,它是一种比基于目标网…

Python 造数据神器Faker

大家好&#xff0c;在编写代码过程中&#xff0c;我们经常需要一些假数据来进行测试或者演示。手动创建这些数据不仅耗时&#xff0c;而且容易出错。幸运的是&#xff0c;Python有一个非常有用的库叫做Faker&#xff0c;它可以生成各种类型的假数据&#xff0c;从名字、地址到公…

教你用U-Mail搭建一个企业邮箱系统

随着互联网的发展&#xff0c;电子邮件已成为企业内部沟通和外部商务的重要工具。对于企业而言&#xff0c;拥有一个安全、稳定、高效的企业邮箱系统至关重要。U-Mail作为一款备受好评的企业邮箱系统&#xff0c;本文将详细介绍如何使用U-Mail从零开始搭建一个企业邮箱系统。 一…

ganglia的安装使用

1.集群内分别安装epel-release依赖&#xff0c;更新yum源 sudo yum -y install epel-release 2&#xff0e;各节点上分别安装gmond sudo yum -y install ganglia-gmond 3.监控节点上安装gmetad和web(这里安装在node1上) sudo yum -y install ganglia-gmetad sudo yum -y insta…

Vue基础(1)数据绑定

一. 文本插值 普通文本可以使用双大括号 {{ }} &#xff0c;要想插入 HTML&#xff0c;需要使用 v-html 指令。 <template><h1>Message: {{ state.msg }}</h1><p>{{ state.count 1 }}</p><p>{{ state.rawHtml }}</p><p v-html…

消息队列实战应用

适用场景 耗时长&#xff0c;非核心业务&#xff0c;生产者不会用到消息处理结果的情况下&#xff0c;可以将消息交给异步服务去缓存与消费 部署MQ服务 version: "3.0" services:rabbitmq:container_name: rabbitmq-15672-1image: rabbitmq:3-managementports:- &…

关于新配置的adb,设备管理器找不到此设备问题

上面页面中一开始没有找到此android设备&#xff0c; 可能是因为我重新配置的adb和设备驱动&#xff0c; 只把adb配置了环境变量&#xff0c;驱动没有更新到电脑中&#xff0c; 点击添加驱动&#xff0c; 选择路径&#xff0c;我安装时都放在了SDK下面&#xff0c;可以尝试…