数据结构之堆(优先级队列)

“愿独立的你,在随心而行的途中,学会释怀而止,而非一时放纵之后而任性非为”

这好像是我第一次写关于数据结构的文章吧,关于数据结构,那真的是太奥秘了,想领略其中的奥秘,必须得付出相应的努力呀!!!

这篇文章呢,将会给大家介绍一种“堆”的数据结构,话不多说,直接开整!

堆:

堆是一种特殊的树形数据结构,通常用于实现优先级队列。堆的特点是满足堆的性质,即每个节点的值都大于或等于(大根堆)或者小于或等于(小根堆)其子节点的值。堆通常以数组的形式实现!

堆的特性:

1.完全二叉树:堆是一种完全二叉树,除最后一层外,其他层的节点都被填满,最后一层的节点从左到右填充。

2.堆性质:

  • 大根堆:每个节点的值都大于或等于其节点的值,根节点是最大值
  • 小根堆:每个节点的值都小于或等于其子节点的值,根节点是最小值

堆的应用:

  • 优先级队列:堆通常用于实现优先级队列,支持高效的插入和删除操作
  • 排序算法:堆排序是一种基于堆的数据结构的排序算法,时间复杂度为O(n*log n)

小根堆示例:

 大根堆示例:

堆化: 

将一个无序数据转换为堆结构。

假设我们有一个无序数组{29,56,34,19,52,16,74,46,92,39},我们怎样将它转换成是一个堆结构呢?

 这里我是以大根堆为例,进行展开下面的内容,我们可以以最后一棵子树进行逐个“回退”地向下调整。定义一个parent记录其父节点,定义一个child记录其子节点。那么我们根据其数组的长度就可以很好的知道其最后一棵子树的父节点和子节点。

父节点:对于节点的索引 i,其父节点的索引可以通过以下公式计算:

                          父节点索引=⌊i−12⌋

这里,i 是当前节点的索引,floor 表示向下取整。

左子节点:对于节点的索引 i,其左子节点的索引可以通过以下公式计算:

                        左子节点索引=2i+1

右子节点:对于节点的索引 i,其右子节点的索引可以通过以下公式计算:

                        右子节点索引=2i+2

 parent依次减减,这样我们就可以遍历到每一棵子树到整棵树。那么在第四次调整的时候,是否也是和前三次调整一样如此轻松呢?

那么在进行调整的时候,我们要注意调整的时候,会不会影响到其他其中一颗子树的结构,所以我们应该继续向下进行调整,那么要继续向下调整到什么时候呢?是不是当我们的child下标大于我们的数组长度的时候,是不是就可以不用继续向下调整了呢?那么基于上述的讲述,我们就可以实现一些代码了! 我们先看看最终的画图结果,然后后面再看看代码运行出来的结果是否和画图上展示的一样。

public class myheap {private int[] elem;//记录数组中元素的个数private int usedSize;public myheap() {elem = new int[10];}//初始化public void init(int[] array) {for (int i = 0; i < array.length; i++) {elem[i] = array[i];usedSize++;}}public void createHeap() {for(int parent = (usedSize-1-1)/2; parent >= 0;parent--) {siftDown(parent,usedSize);}}private void siftDown(int parent, int end) {//计算孩子节点的下标int child = parent*2+1;while(child < end) {//先找到孩子节点中较大值的孩子节点下标(注意此处右孩子是否存在)if(child+1 < end && elem[child] < elem[child+1]) {child++;}//比较孩子节点和父节点的值,如果父节点小于子节点的值,就进行交换if(elem[parent] < elem[child]) {swap(child,parent);//交换之后,继续进行向下调整parent = child;child = parent*2+1;}else{break;}}}//交换private void swap(int child, int parent) {int tmp = elem[child];elem[child] = elem[parent];elem[parent] = tmp;}
}

测试代码:

public class testMyHeap {public static void main(String[] args) {int[] array = new int[]{29,56,34,19,52,16,74,46,92,39};myheap  heap = new myheap();heap.init(array);heap.createHeap();}
}

接下来进行打断点来看看是否和上面的图中结果一样:

结果是一样的,所以说没有问题。 

堆插入:

将新元素添加到堆中,通常将其放在数组的末尾。

在进行插入的时候,我们首先要考虑的问题应该是数组是否还有空间进行插入,如果不够的话,便进行扩容!!!

插入的时候,我们应该怎么进行调整呢?如果我们新插入的节点比在其父节点小呢?因为此时我们是一个大根堆的形式,所以这种情况下,不需要进行任何处理。那如果是插入的值很大呢,我们是不是要和在其父节点进行交换之后,然后再往上面继续进行调整呢?那么也是要考虑何时能够停止,是不是应该是parent小于0时就结束此次向上调整呢?所以我们的堆插入也是很简单的!

假设我们依次插入数据{29,56,34,19,52},我们先来画画图,以便验证后续的代码。

public void offer(int key) {if(isFull()) {elem = Arrays.copyOf(elem,2* elem.length);}elem[usedSize] = key;usedSize++;siftUp(usedSize-1);}private void siftUp(int child) {int parent = (child-1)/2;while(parent >= 0) {if(elem[parent] < elem[child]) {swap(child,parent);//交换之后,继续向上调整child = parent;parent = (child-1)/2;}else{break;}}}private boolean isFull() {return usedSize == elem.length;}

 

 堆删除:

删除的时候,一般是删除“堆顶”元素。

在删除堆顶元素时,我们一般的操作是先将堆尾元素与堆顶元素进行交换后,那么交换之后呢,我们就从0位置开始向下进行调整了,没有影响到的其他子树,我们不需要管。基于上面的知识后,对于这个删除来说就不用多谈了,直接上代码。

public int poll() {if(isEmpty()) {return -1;}int ret = elem[0];swap(0,usedSize-1);usedSize--;siftDown(0,usedSize);return ret;}private boolean isEmpty() {return usedSize == 0;}

堆排序:

堆排序是一种基于比较的排序算法,利用堆这种数据结构来实现排序。

假设我们是进行从小到大进行排序,因为堆排序是基于堆来实现的,所以当我们弄好一个堆结构时,我们再进行排序的操作(大根堆为例)。此时是一个大根堆,那么堆顶元素就是最大的数了,此时我们就交换堆顶元素和堆尾元素,那么最大的数就到了最后面了,这个位置就是它该到的位置。我们调整了0位置,所以要从0位置开始向下进行调整,使后面的最大的数到堆顶后,我们重复上述的步骤即可!

public void sortHeap() {int endIndex = usedSize-1;while(endIndex > 0) {swap(0,endIndex);siftDown(0,endIndex);endIndex--;}}

认识堆之后,我们来简单认识一下优先级队列!

优先级队列:

数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象。这种数
据结构就是优先级队列(Priority Queue),JDK1.8中的PriorityQueue底层使用了堆这种数据结构。

关于PriorityQueue的使用的注意事项:
1. 使用时必须导入PriorityQueue所在的包
2. PriorityQueue中放置的元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出
ClassCastException异常


3. 不能插入null对象,否则会抛出NullPointerException


4. 没有容量限制,可以插入任意多个元素,其内部可以自动扩容
7. PriorityQueue默认情况下是小堆---即每次获取到的元素都是最小的元素

从输出的结果中可以看出,默认是以小根堆的形式创建的。那么如果我们想要建立一个大根堆呢,其实也是可以的。

  • 希望通过本篇博客,读者能够对堆有一个更好的理解。

本期节目到此结束,拜拜!!!

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

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

相关文章

C++:类和对象OJ题

目录 一、求123...n 二、计算日期到天数的转换 三、日期差值 四、打印日期 一、求123...n 这里先把题目链接放在这里求123.....n 描述&#xff1a; 求123...n&#xff0c;要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句&#xff08;A?B:C…

UnLua实现继承

一、在蓝图中实现继承 1、创建父类&#xff0c;并绑定Lua脚本 2、创建子类蓝图&#xff0c;如果先创建的子类&#xff0c;可以修改父类继承 注意&#xff0c;提示选择继承父类的接口&#xff01; 二、在Lua中实现继承 1、在父类Lua脚本中实现函数 BP_CharacterBase.lua func…

一劳永逸:用脚本实现夸克网盘内容自动更新

系统环境&#xff1a;debian/ubuntu 、 安装了python3 原作者项目&#xff1a;https://github.com/Cp0204/quark-auto-save 感谢 缘起 我喜欢看电影追剧&#xff0c;会经常转存一些资源到夸克网盘&#xff0c;电影还好&#xff0c;如果是电视剧&#xff0c;麻烦就来了。 对于一…

【STL】 set 与 multiset:基础、操作与应用

在 C 标准库中&#xff0c;set 和 multiset 是两个非常常见的关联容器&#xff0c;主要用于存储和管理具有一定规则的数据集合。本文将详细讲解如何使用这两个容器&#xff0c;并结合实例代码&#xff0c;分析其操作和特性。 0.基础操作概览 0.1.构造&#xff1a; set<T&…

深度学习:神经网络--手写数字识别

目录 一、datasets 1.datasets简介 2.主要特点 二、MNIST 三、使用神经网络实现手写数字识别 1.创建数据加载器 2.判断是否使用GPU 3.创建神经网络 4.创建训练集模型 5.创建测试集模型 6.创建损失函数和优化器并训练 一、datasets 1.datasets简介 datasets是一个广…

[mysql]mysql排序和分页

#排序和分页本身是两块内容,因为都比较简单,我们就把它分到通一个内容里. #1排序: SELECT * FROM employees #我们会发现,我们没有做排序操作,但是最后出来的107条结果还是会按顺序发出,而且是每次都一样.这我们就有一个疑惑了,现在我们的数据库是根据什么来排序的,在我们没有进…

windows 驱动实例分析系列-COM驱动案例讲解

COM也被称之为串口,这是一种非常简单的通讯接口,这种结构简单的接口被广泛的应用在开发中,几乎所有系统都能支持这种通讯接口,它有RS232和RS485等分支,但一般我们都会使用RS232作为常见的串口,因为它足够简单和高效。 几乎所有的开发板,都会提供用于烧录、调试、日志的…

redis为什么不使用一致性hash

Redis节点间通信时&#xff0c;心跳包会携带节点的所有槽信息&#xff0c;它能以幂等方式来更新配置。如果采用 16384 个插槽&#xff0c;占空间 2KB (16384/8);如果采用 65536 个插槽&#xff0c;占空间 8KB (65536/8)。 今天我们聊个知识点为什么Redis使用哈希槽而不是一致性…

FastAPI 的隐藏宝石:自动生成 TypeScript 客户端

在现代 Web 开发中&#xff0c;前后端分离已成为标准做法。这种架构允许前端和后端独立开发和扩展&#xff0c;但同时也带来了如何高效交互的问题。FastAPI&#xff0c;作为一个新兴的 Python Web 框架&#xff0c;提供了一个优雅的解决方案&#xff1a;自动生成客户端代码。本…

引领长期投资新篇章:价值增长与财务安全的双重保障

随着全球金融市场的不断演变&#xff0c;长期投资策略因其稳健性和对价值增长的显著推动作用而日益受到投资者的重视。在这一背景下&#xff0c;Zeal Digital Shares&#xff08;ZDS&#xff09;项目以其创新的数字股票产品&#xff0c;为全球投资者提供了一个全新的长期投资平…

re题(38)BUUCTF-[FlareOn6]Overlong

BUUCTF在线评测 (buuoj.cn) 运行一下.exe文件 查壳是32位的文件&#xff0c;放到ida反汇编 对unk_402008前28位进行一个操作&#xff0c;我们看到运行.exe文件的窗口正好是28个字符&#xff0c;而unk_402008中不止28个数据&#xff0c;所以猜测MessageBoxA&#xff08;&#x…

cv中每个patch的关联

在计算机视觉任务中&#xff0c;当图像被划分为多个小块&#xff08;patches&#xff09;时&#xff0c;每个 patch 的关联性可以通过不同的方法来计算。具体取决于使用的模型和任务&#xff0c;以下是一些常见的计算 patch 关联性的方法&#xff1a; 1. Vision Transformer (…

Shell运行原理与Linux权限概念

shell的运行原理 Linux严格意义上说的是一个操作系统。我们称之为“核心&#xff08;kernel&#xff09;”&#xff0c;但我们一般用户&#xff0c;不能直接使用kernel&#xff0c;二十通过kernel的“外壳”程序&#xff0c;也就是所谓的shell&#xff0c;来与kernel沟通。 从…

网络穿透:TCP 打洞、UDP 打洞与 UPnP

在现代网络中&#xff0c;很多设备都处于 NAT&#xff08;网络地址转换&#xff09;或防火墙后面&#xff0c;这使得直接访问这些设备变得困难。在这种情况下&#xff0c;网络穿透技术就显得非常重要。本文将介绍三种常用的网络穿透技术&#xff1a;TCP 打洞、UDP 打洞和 UPnP。…

qt-C++笔记之作用等同的宏和关键字

qt-C笔记之作用等同的宏和关键字 code review! Q_SLOT 和 slots&#xff1a; Q_SLOT是slots的替代宏&#xff0c;用于声明槽函数。 Q_SIGNAL 和 signals&#xff1a; Q_SIGNAL类似于signals&#xff0c;用于声明信号。 Q_EMIT 和 emit&#xff1a; Q_EMIT 是 Qt 中用于发射…

18.2K Star,AI 高效视频监控摄像头

Hi&#xff0c;骚年&#xff0c;我是大 G&#xff0c;公众号「GitHub 指北」会推荐 GitHub 上有趣有用的项目&#xff0c;一分钟 get 一个优秀的开源项目&#xff0c;挖掘开源的价值&#xff0c;欢迎关注。 导语 在家庭和企业安防领域&#xff0c;实时视频监控是保障安全的核…

AIGC8: 高通骁龙AIPC开发者大会记录B

图中是一个小男孩在市场卖他的作品。 AI应用开发出来之后&#xff0c;无论是个人开发者还是企业开发者。 如何推广分发是面临的大问题。 做出来的东西一定要符合商业规律。否则就是实验室里面的玩物&#xff0c;或者自嗨的东西。 背景 上次是回顾和思考前面两个硬件营销总的…

【JVM】类加载

1. 类加载过程 Java虚拟机&#xff08;JVM&#xff09;的 类加载 过程是将字节码文件&#xff08;.class文件&#xff09;从存储设备加载到内存&#xff0c;并为其创建相应的类对象的过程。类加载是Java程序运行的基础&#xff0c;保证了程序的动态性和安全性。JVM的类加载过程…

人工智能 | 基于ChatGPT开发人工智能服务平台

简介 ChatGPT 在刚问世的时候&#xff0c;其产品形态就是一个问答机器人。而基于ChatGPT的能力还可以对其做一些二次开发和拓展。比如模拟面试功能、或者智能机器人功能。 模拟面试功能包括个性化问题生成、实时反馈、多轮面试模拟、面试报告。 智能机器人功能提供24/7客服支…

将阮一峰老师的《ES6入门教程》的源码拷贝本地运行和发布

你好同学&#xff0c;我是沐爸&#xff0c;欢迎点赞、收藏、评论和关注。 阮一峰老师的《ES6入门教程》应该是很多同学学习 ES6 知识的重要参考吧&#xff0c;应该也有很多同学在看该文档的时候&#xff0c;想知道这个教程的前端源码是怎么实现的&#xff0c;也可能有同学下载…