数据结构(Java):优先级队列(堆)堆的模拟实现

目录

1、优先级队列

1.1 概念

1.2 PriorityQueue底层结构

2、 堆

2.1 堆的概念

 2.2 堆的存储结构

3、优先级队列(堆)的模拟实现

3.1 堆的创建

3.1.1 向下调整算法建完整堆

3.2 堆的插入 

3.2.1 向上调整算法

 3.3 堆的删除

3.4 堆排序


1、优先级队列

1.1 概念

对于队列而言,数据只能从队尾进,队头出,遵循着固定的先进先出原则。

而在某些特殊场景需求下,要求优先级高的元素先出队列,

在这种情况下,数据结构应该提供两个最基本的操作:

  • ①:不仅能添加新对象
  • ②:还能返回最高优先级对象。

这种数据结构就是优先级队列,Java也提供了PriorityQueue集合。 

1.2 PriorityQueue底层结构

PriorityQueue底层是堆这种数据结构,而堆实际就是在完全二叉树的基础上进行了一些调整,接下来,让我们来聊一聊堆到底是什么。


2、 堆

2.1 堆的概念

 所有元素按完全二叉树的顺序存储方式存储在一个一维数组中。

堆分为 大根堆与小根堆。

大根堆:每个节点都大于或等于其子节点。

小根堆:每个节点都小于或等于其子节点。


 2.2 堆的存储结构

我们已知堆为一棵完全二叉树,故采用顺序的方式存储在数组中。

而对于我们之前所学的二叉树,为什么没有采用顺序存储而是采用链式存储呢?

因为二叉树并非都为完全二叉树,若采用顺序存储会造成空间浪费:

因为堆采用顺序的方式进行存储,且为完全二叉树,故具有以下性质:

  1. 孩子节点下标为i,则其父亲节点下标为(i - 1)/2
  2. 父亲下标为i,则其左孩子下标为2i+1(2i+1<节点总数的情况下,否则无左孩子)
  3. 父亲下标为i,则其右孩子下标为2i+2(2i+2<节点总数的情况下,否则无右孩子)

3、优先级队列(堆)的模拟实现

注:下文中代码实现的为大根堆

3.1 堆的创建

 向下调整算法

向下调整算法:选出其左右孩子中值较小值元素(建大堆就选较大元素,建小堆就选较小元素,这里以建小堆为例),将这个元素和根节点进行比较,若比根节点还小,就和根节点交换,交换后可能导致子树不满足堆的性质,因此需要继续向下调整。

注意:向下调整算法的使用,必须要求其左右子树必须为大根堆或者小根堆!!!

向下调整算法的时间复杂度为:O(logN),因为最坏情况是从根一路比较到叶子,比较的次数即为完全二叉树的高度次。

3.1.1 向下调整算法建完整堆

我们给出一组数据:{ 27,15,19,18,28,34,65,49,25,37 },要想将这组数据建成堆,我们可以使用向下调整法建堆。

而一组乱序数据其左右子树是不为堆的,那就需要通过向下调整算法从倒数第一个非叶子节点(下标为(数组.length-1-1)/2 )开始建堆,直到将整棵树建成堆。

建堆的时间复杂度为:O(N)!!!,

什么不是O(N*logN)呢?这里给出解释:

向下调整整体建堆代码:

/*** 建堆整体时间复杂度:O(N)*/public void createHeap() {//从倒数第一个飞非叶子节点开始建堆int parent = (this.usedSize - 1 - 1)/2;while (parent >= 0) {//O(N)siftDown(parent,this.usedSize);parent--;}}/***向下调整算法* 时间复杂度:O(logN)* @param parent 向下调整的起始位置,即根节点* @param usedSize 标定调整的结束 若算出的孩子节点坐标>=usedSize时,说明已调整完成*/private void siftDown(int parent, int usedSize) {int child = parent*2+1;//当没有孩子节点时,说明向下调整完成while (child < usedSize) {//当右孩子存在时,选出左右孩子的最大值(建大堆)if (child + 1 < usedSize) {if (elem[child] < elem[child + 1]) {child = child + 1;}}//和根节点进行比较if (elem[child] > elem[parent]) {swap(elem, parent, child);parent = child;child = parent*2+1;}else {//若根节点大,说明已是大堆,break结束break;}}}

3.2 堆的插入 

插入数据总共有两个步骤:

  1. 将新元素插入堆的末尾(插入后不再为堆)
  2. 使用向上调整算法调整为堆 

3.2.1 向上调整算法

(这里以建小堆为例):将元素插入到堆的末尾后,和根节点进行比较,如果比根节点还小就和根节点换,继续向上调整,直至满足堆的性质。如果没有根节点小,说明此时已满足堆的性质,调整完成。

时间复杂度:O(logN)

插入元素代码:

/*** 插入元素* 时间复杂度:O(logN)* @param x:新插入元素的值*/public void offer(int x) {if (isFull()) {grow();}elem[usedSize] = x;siftUp(usedSize);usedSize++;}/*** 向下调整算法* @param childIndex:新插入元素的下标*/public void siftUp(int childIndex) {//找到新节点的父节点的下标int parent = (childIndex - 1)/2;//parent为负数时,说明调整完成(最坏)while (parent >= 0) {if (elem[parent] < elem[childIndex]) {swap(elem,parent,childIndex);childIndex = parent;parent = (childIndex - 1)/2;}else {break;}}}/*** 如果堆底层的数组满了,就扩容*/private void grow() {this.elem = Arrays.copyOf(elem,elem.length*2);}

向上调整算法建堆【拓展】 

故我们也可以一个一个的插入元素(使用向上调整算法)来建堆,但是不推荐,因为时间复杂度比向下调整算法建堆要大,为:O(N*logN)

其实主要原因就是:最后一层的元素是最多的,都要一个个向上调整


 3.3 堆的删除

堆的删除一定删除的是堆顶元素。

故,删除元素的思想很简单,即:

  1.  将堆顶元素和堆中最后一个元素交换
  2.  将堆中有效数据个数(usedSize)减一
  3.  对堆顶元素进行向下调整(只需要调整0下标就可以了)

 堆删除代码:

/*** 删除堆元素*/public void poll() {if (isEmpty()) {//如果堆为空,返回return;}//交换堆顶和最后一个元素swap(elem,0,usedSize-1);//堆元素有效个数-1usedSize--;//只向下调整0下标即可siftDown(0,usedSize);}

3.4 堆排序

若要升序排列,要建大根堆;若要降序排列,就要建小根堆。

以排升序为例:若要排升序,则为大堆,排序过程如下:

  1. 将堆顶元素和堆末元素交换,有效数据个数减一(因为堆顶元素为最大值元素,此时最大值元素已来到数组末尾)
  2. 将0下标处向下调整,重新调整为大堆
  3. 继续将堆顶元素和堆末元素交换,有效数据个数减一(堆顶元素为次大值元素,此时次大值元素已来到数组末尾倒数二个位置处)
  4. 将0下标处向下调整,重新调整为大堆
  5. 重复以上过程,直至只剩一个元素的时候,此时数组已有序且为升序排列

堆排序 排升序代码 :

/*** 堆排序*/public void heapSort() {if (isEmpty()) {return;}int end = usedSize-1;while (end > 0) {swap(elem,0,end);siftDown(0,end);end--;}}

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

51单片机嵌入式开发:12、STC89C52RC 红外解码数码管显示

STC89C52RC 红外解码数码管显示 1 概述2 HX1838原理2.1 原理概述2.2 原理概述 3 HX1838代码实现3.1 工程整理3.2 工程代码3.3 演示 4 HX1838总结 1 概述 HX1838是一种常见的红外接收模块&#xff0c;用于接收和解码红外遥控器发送的红外信号。 HX1838具有以下特点和功能&#…

二、BIO、NIO、直接内存与零拷贝

一、网络通信编程基础 1、Socket Socket是应用层与TCP/IP协议族通信的中间软件抽象层&#xff0c;是一组接口&#xff0c;由操作系统提供&#xff1b; Socket将复杂的TCP/IP协议处理和通信缓存管理都隐藏在接口后面&#xff0c;对用户来说就是使用简单的接口进行网络应用编程…

HarmonyOS根据官网写案列~ArkTs从简单地页面开始

Entry Component struct Index {State message: string 快速入门;build() {Column() {Text(this.message).fontSize(24).fontWeight(700).width(100%).textAlign(TextAlign.Start).padding({ left: 16 }).fontFamily(HarmonyHeiTi-Bold).lineHeight(33)Scroll() {Column() {Ba…

使用Docker 实现 MySQL 循环复制(三)

系列文章 使用Docker 实现 MySQL 循环复制&#xff08;一&#xff09; 使用Docker 实现 MySQL 循环复制&#xff08;二&#xff09; 目录 系列文章1. 在主机上安装MySQL客户端2. 配置循环复制拓扑2.1 进入容器2.2 创建复制用户并授予复制权限2.3 复位二进制日志2.4 配置环形复…

信息安全工程师题

物理隔离技术要求两台物理机物理上并不直连&#xff0c;只能进行间接的信息交换。所以防火墙不能实现网络的物理隔离Web应用防火墙可以防止SQL注入、xss攻击、恶意文件上传、远程命令执行、文件包含、恶意扫描拦截等&#xff1b;可以发现并拦截恶意的Web代码&#xff1b;可防止…

详解MLOps,从Jupyter开发到生产部署

大家好&#xff0c;Jupyter notebook 是机器学习的便捷工具&#xff0c;但在应用部署方面存在局限。为了提升其可扩展性和稳定性&#xff0c;需结合DevOps和MLOps技术。通过自动化的持续集成和持续交付流程&#xff0c;可将AI应用高效部署至HuggingFace平台。 本文将介绍MLOps…

色彩与故乡的对话 —— 钱华个人油画展正式开展

色彩与故乡的对话 —— 钱华个人油画展正式开展 2024年7月17日 &#xff0c;在宁波这座历史与现代交织的城市里&#xff0c;艺术与文化的碰撞再次绽放出耀眼的光芒。由宁波海曙区美术家协会主办&#xff0c;宁波市海纳广场开发经营有限公司协办的“色彩与故乡的对话——钱华个人…

HDLC(高级数据链路控制协议)的定义、数据结构、状态检测、基本配置、特点及限制

一、HDLC的定义 HDLC是一种面向比特的对用同步串行数字链路封装协议。 面向比特:对于任何比特流,HDLC都可以实现透明的传输; 同步串行:应用于同步串行线路; 应用于接口:在同步模式下的Serial接口和pos接口; 只支持点到点链路,通过keepalive报文来检测链路状态。 …

pnpm build打包时占内溢出

这两天在打包H5网页的时候失败&#xff0c;总是提示下方错误 FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory 严重错误&#xff1a;堆限制附近标记压缩无效分配失败 - JavaScript 堆内存不足 尝试了多种方法&…

使用Docker 实现 MySQL 循环复制(二)

系列文章 使用Docker 实现 MySQL 循环复制&#xff08;一&#xff09; 目录 系列文章1. 创建三个 mysql 容器1.1 准备三个 mysql 容器的挂载卷1.2 为三个mysql实例创建配置文件1.3 修改各目录的权限以满足 mysql 容器的要求1.4 创建 docker-compose.yaml 文件1.5 创建容器 1. …

记录一下在Hyper-v中动态磁盘在Ubuntu中不完全用到的问题(扩展根目录)

在之前给hyper虚拟机的Ubuntu分配磁盘有20G&#xff1b; 后来在Ubuntu中查看磁盘发现有一个分区没用到&#xff1a; 贴的图片是完成扩展后的 之前这里是10G&#xff0c;然后有个dev/sda4的分区&#xff0c;也是10G&#xff0c;Type是Microsoft Basic Data&#xff1b; …

实现了一个心理测试的小程序,微信小程序学习使用问题总结

1. 如何在跳转页面中传递参数 &#xff0c;在 onLoad 方法中通过 options 接收 2. radio 如何获取选中的值&#xff1f; bindchange 方法 参数e, e.detail.value 。 如果想要获取其他属性&#xff0c;使用data-xx 指定&#xff0c;然后 e.target.dataset.xx 获取。 3. 不刷…

项目实用linux 操作详解-轻松玩转linux

我之前写过完整的linux系统详解介绍&#xff1a; LInux操作详解一&#xff1a;vmware安装linux系统以及网络配置 LInux操作详解二&#xff1a;linux的目录结构 LInux操作详解三&#xff1a;linux实际操作及远程登录 LInux操作详解四&#xff1a;linux的vi和vim编辑器 LInux操作…

剧本杀小程序搭建,为商家带来新的收益方向

近几年&#xff0c;剧本杀游戏成为了游戏市场的一匹黑马&#xff0c;受到了不少年轻玩家的欢迎。随着信息技术的快速发展&#xff0c;传统的剧本杀门店已经无法满足游戏玩家日益增长的需求&#xff0c;因此&#xff0c;剧本杀市场开始向线上模式发展&#xff0c;实现行业数字化…

灵雀云AML:赋能金融AI,构建数智时代核心竞争力

在人工智能&#xff08;AI&#xff09;技术的迅猛发展中&#xff0c;金融行业正迈入变革的新时代。AI不仅在优化投资决策、信用评估、实时监控和欺诈识别方面展现出强大功能&#xff0c;还极大地提升了客户体验、降低了运营成本&#xff0c;并推动了产品创新。面对智能时代的挑…

唯众特色软件开发实训室解决方案

特色软件开发实训室解决方案旨在构建一个全方位、高度灵活且效率卓越的实训生态系统&#xff0c;它不仅仅是一个学习空间&#xff0c;更是一个让学生能够身临其境&#xff0c;在模拟真实工作环境中锤炼技能的实战场。通过精心设计的实训项目与案例&#xff0c;学生能够在实践中…

在Ubuntu上安装redis

Ubuntu上安装redis 一、通过下载redis的压缩包安装二、通过apt包管理器安装Redis三、修改redis的配置文件四、控制redis启动 Redis是一种开源的内存数据存储&#xff0c;可以用作数据库、缓存和消息代理等。本文将会介绍两种不同的安装方式&#xff0c;包括通过压缩包安装以及通…

日志之ELK使用讲解

文章目录 1 ELK1.1 ELK 简介1.1.1 Logstash1.1.2 Elasticsearch1.1.3 Kibana 1.2 ELK 实现方案1.3 ELK 平台搭建1.3.1 安装 Logstash1.3.2 安装 Elasticsearch1.3.3 安装 Kibana 1.4 在 SpringBoot 中使用 ELK1.4.1 修改并部署 Spring Boot 项目1.4.2 配置 Shipper 角色 Logsta…

国内从事双臂机器人的团队

一、背景 随着人形机器人的发展&#xff0c;双臂协同操作得到了越来越多研究人员的关注。我自己也是做双臂机器人方向的&#xff0c;虽然通过看论文或刷知乎了解到国内有许多团队在做双臂机器人方向&#xff0c;但还没有系统的整理过&#xff0c;因此趁这次机会&#xff0c;好…

【C++进阶学习】第七弹——AVL树——树形结构存储数据的经典模块

二叉搜索树&#xff1a;【C进阶学习】第五弹——二叉搜索树——二叉树进阶及set和map的铺垫-CSDN博客 目录 一、AVL树的概念 二、AVL树的原理与实现 AVL树的节点 AVL树的插入 AVL树的旋转 AVL树的打印 AVL树的检查 三、实现AVL树的完整代码 四、总结 前言&#xff1a…