当前位置: 首页 > article >正文

数据结构 堆与优先级队列

文章目录

    • 📕1. 堆(Heap)
        • ✏️1.1 堆的概念
        • ✏️1.2 堆的存储方式
        • ✏️1.3 堆的创建
        • ✏️1.4 堆的插入
        • ✏️1.5 堆的删除
    • 📕2. 优先级队列(PriorityQueue)
        • ✏️2.1 堆与优先级队列的关系
        • ✏️2.2 优先级队列的构造方法
        • ✏️2.3 优先级队列的常用方法
    • 3. Java对象的比较
        • 3.1 基于Comparble接口类的比较
        • 3.2 基于比较器比较

📕1. 堆(Heap)

✏️1.1 堆的概念

堆(Heap)是一种特殊的完全二叉树,它满足父节点的值总是不大于或不小于其子节点的值。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

堆满足以下两个性质:

1.堆中某个节点的值总是不大于或不小于其根节点的值
2. 堆总是一棵完全二叉树

在这里插入图片描述

✏️1.2 堆的存储方式

从堆的概念可知,堆是一颗完全二叉树,因此可以层序的规则采用顺序的方式来高效存储

对与非完全二叉树,则不适合使用顺序的方式进行存储,因为为了能够还原二叉树,空间中必须要存储空节点,这会导致空间利用率比较低.

在这里插入图片描述
将元素存储到数组中后,假设i为节点在数组中的下标,则有:

  1. 如果i为0,则i表示的节点为根节点,否则i节点的双亲节点为 (i - 1)/2
  2. 如果2 * i + 1 小于节点个数,则节点i的左孩子下标为2 * i + 1,否则没有左孩子
  3. 如果2 * i + 2 小于节点个数,则节点i的右孩子下标为2 * i + 2,否则没有右孩子
✏️1.3 堆的创建

想要创建堆,我们首先要了解向下调整(这里以创建小根堆为例):

  1. 我们将想要调整的节点标记为parent,child为该节点的左孩子(如果有孩子节点,则一定是现有左孩子)
  2. 如果有左孩子节点,则判断child<size(数组元素个数),然后进行以下操作,直到child<size条件不成立
    2.1 判断parent的右孩子是否存在,找到左右孩子中最小的那个,用child标记.(如果右孩子比左孩子小,child+=1)
    2.2 将parent与child比较,如果parent<child,则调整结束;否则进行元素交换,交换完之后,有可能新构建的子树又不满足小根堆,所以需要将parent = child,child = parent * 2 + 1. 然后继续重复2操作.这便是向下调整.
    public void siftDown(int[] arr,int parent){//此时child标记的左孩子,因为parent右孩子结点的话只能先有左孩子int child = parent * 2 + 1;int size = arr.length;while(child < size){// 如果右孩⼦存在,找到左右孩⼦中较⼩的孩⼦,⽤child进⾏标记if (child+1 < size && arr[child] < arr[child+1]){child+=1;}// 如果双亲⽐其最⼩的孩⼦还⼩,说明该结构已经满⾜堆的特性了if (arr[parent] <= arr[child]){break;}else {// 将双亲与较⼩的孩⼦交换int t = arr[parent];arr[parent] = arr[child];arr[child] = t;// parent中⼤的元素往下移动,可能会造成⼦树不满⾜堆的性质,因此需要继续向下调整parent = child;child = parent * 2 + 1;}}}

了解完向下调整,那给你任意一些数据,那我们该如何创建成小根堆呢?

public void createHeap(int[] arr){// 找倒数第⼀个⾮叶⼦节点,从该节点位置开始往前⼀直到根节点,遇到⼀个节点,应⽤向下调整int root = (arr.length-2)/2;for (; root >= 0 ; root--) {siftDown(arr,root);}
}

还需要注意的是,建堆的时间复杂度为O(N);

✏️1.4 堆的插入

堆的插入总共需要两个步骤;

  1. 先将元素放入到数组最后一个空间中(数组长度不够时需要扩容)
  2. 将新插入的节点利用向上调整,直到满足堆的性质

在这里插入图片描述

向上调整:

public void siftUp(int child){//以创建小根堆为例//找到child的根节点int parent = (child-1)/2;while(child > 0){// 如果双亲⽐孩⼦小,parent满⾜堆的性质,调整结束if (arr[parent] < arr[child]){break;}else {// 将双亲与孩⼦节点进⾏交换int tmp = arr[parent];arr[parent] = arr[child];arr[child] = arr[tmp];}// ⼩的元素向下移动,调整后的⼦树可能不满⾜堆的性质,因此需要继续向上调增child = parent;parent = (child-1)/2;}}

插入数据:

/**
* 插入数据:
* 每次插入到当前堆的最后一个(数组的最后一个),然后向上调整
*/public void offer(int val) {if(isFull()) {elem = Arrays.copyOf(elem,2*elem.length);}elem[size] = val;//调整siftUp(size);size++;}
✏️1.5 堆的删除

堆的插入总共需要两个步骤;

  1. 将堆顶元素与堆中最后一个元素交换
  2. 将对中有效数据减少一个
  3. 对堆顶元素进行向下调整

注意:堆的删除⼀定删除的是堆顶元素

📕2. 优先级队列(PriorityQueue)

之前我们已经学习过队列了,队列是一种先进先出(FIFO)的数据结构,但有些情况下,操作的数据可能带有优先级,出队列时,可能需要优先级高的元素先出队列 . 比如:在手机上玩游戏的时候,如果有来电,那么系统应该优先处理打进来的电话.该场景下,使用队列显然不合适,因此数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,⼀个是添加新的对象。这种数据结构就是优先级队列(PriorityQueue).

✏️2.1 堆与优先级队列的关系

首先大家要知道一点 , 堆是一种数据结构 , 而优先级队列也是一种数据结构 , 二者是完全单独且不同的数据结构.
优先队列本身就是一种数据结构,它的着重点是优先,而不是队列。优先是目标,队列只是形式.
优先级队列不仅可以用堆来实现 , 用链表也是可以模拟实现的 , 只不过在JDK1.8中 , PriorityQueue的底层实现使用了堆这种数据结构.

✏️2.2 优先级队列的构造方法

在这里插入图片描述

注意:默认情况下,PriorityQueue队列是小堆,如果需要大堆需要用户提供比较器

✏️2.3 优先级队列的常用方法

在这里插入图片描述
优先级队列的扩容说明:
• 如果容量小于64时,是按照oldCapacity的2倍方式扩容的
• 如果容量大于等于64,是按照oldCapacity的1.5倍方式扩容的
• 如果容量超过MAX_ARRAY_SIZE,按照MAX_ARRAY_SIZE来进行扩容

3. Java对象的比较

在SE阶段中,我们介绍了引用类型变量使用equals()方法进行比较 , 基本数据类型变量使用大于号小于号进行比较 , 故以上两种方法此处不再赘述.

3.1 基于Comparble接口类的比较

Comparble是JDK提供的泛型的比较接口类,源码实现具体如下:

public interface Comparable<E> {// 返回值:// < 0: 表⽰ this 指向的对象⼩于 o 指向的对象// == 0: 表⽰ this 指向的对象等于 o 指向的对象// > 0: 表⽰ this 指向的对象⼤于 o 指向的对象int compareTo(E o);
}

对于用户定义类型,如果要想按照大小的方式进行比较时:
在定义类时,实现Comparble接口即可,然后在类中重写compareTo方法。

public class Card implements Comparable<Card> {public int rank; // 数值public String suit; // 花⾊public Card(int rank, String suit) {this.rank = rank;this.suit = suit;}// 根据数值⽐较,不管花⾊// 这⾥我们认为 null 是最⼩的@Overridepublic int compareTo(Card o) {if (o == null) {return 1;}return this.rank - o.rank;}public static void main(String[] args){Card p = new Card(1, "♠");Card q = new Card(2, "♠");Card o = new Card(1, "♠");System.out.println(p.compareTo(o)); // == 0,表⽰牌相等System.out.println(p.compareTo(q)); // < 0,表⽰ p ⽐较⼩System.out.println(q.compareTo(p)); // > 0,表⽰ q ⽐较⼤}
}
3.2 基于比较器比较

Comparator是java.util 包中的泛型接口类 , 源码如下:

public interface Comparator<T> {// 返回值:// < 0: 表⽰ o1 指向的对象⼩于 o2 指向的对象// == 0: 表⽰ o1 指向的对象等于 o2 指向的对象// > 0: 表⽰ o1 指向的对象等于 o2 指向的对象int compare(T o1, T o2);
}

按照比较器方式进行比较,具体步骤如下:

  1. 用户自定义比较器类,实现Comparator接口
  2. 重写Comparator中的compare方法
import java.util.Comparator;class Card {public int rank; // 数值public String suit; // 花⾊public Card(int rank, String suit) {this.rank = rank;this.suit = suit;}
}class CardComparator implements Comparator<Card> {// 根据数值⽐较,不管花⾊// 这⾥我们认为 null 是最⼩的@Overridepublic int compare(Card o1, Card o2) {if (o1 == o2) {return 0;}if (o1 == null) {return -1;}if (o2 == null) {return 1;}return o1.rank - o2.rank;}public static void main(String[] args){Card p = new Card(1, "♠");Card q = new Card(2, "♠");Card o = new Card(1, "♠");// 定义⽐较器对象CardComparator cmptor = new CardComparator();// 使⽤⽐较器对象进⾏⽐较System.out.println(cmptor.compare(p, o)); // == 0,表⽰牌相等System.out.println(cmptor.compare(p, q)); // < 0,表⽰ p ⽐较⼩System.out.println(cmptor.compare(q, p)); // > 0,表⽰ q ⽐较⼤}
}

相关文章:

数据结构 堆与优先级队列

文章目录 &#x1f4d5;1. 堆(Heap)✏️1.1 堆的概念✏️1.2 堆的存储方式✏️1.3 堆的创建✏️1.4 堆的插入✏️1.5 堆的删除 &#x1f4d5;2. 优先级队列(PriorityQueue)✏️2.1 堆与优先级队列的关系✏️2.2 优先级队列的构造方法✏️2.3 优先级队列的常用方法 3. Java对象的…...

Leetcode 3569. Maximize Count of Distinct Primes After Split

Leetcode 3569. Maximize Count of Distinct Primes After Split 1. 解题思路2. 代码实现 题目链接&#xff1a;3569. Maximize Count of Distinct Primes After Split 1. 解题思路 这一题的话思路倒是还好&#xff0c;显然&#xff0c;要找出所有distinct的质数的切分&…...

用好 ImageFX,解锁游戏素材生成新姿势:从入门到进阶

用好 ImageFX&#xff0c;解锁游戏素材生成新姿势&#xff1a;从入门到进阶 (备注)大陆ip无法访问到imagefx 地址:https://labs.google/fx/zh/tools/image-fx 对于独立游戏开发者和小型团队而言&#xff0c;美术资源往往是项目推进中的一大痛点。预算有限、专业美术人员缺乏…...

unix/linux,sudo,其基本属性、语法、操作、api

现在我们要深入到sudo的“微观结构”了——它的属性、语法、操作以及是否有传统意义上的“API”。这就像我们从宏观的宇宙现象深入到基本粒子的相互作用一样,充满了探索的乐趣! 一、 sudo 的基本属性 (Fundamental Attributes) 这些属性是sudo作为一款软件和系统工具的核心…...

文本内容变化引起布局尺寸变化 导致的 UI 适配问题

在使用 Flutter 开发应用时&#xff0c;配合 easy_localization 实现多语言切换是一个非常常见的做法。但正如你所说&#xff0c;在不同语言下文字长度差异较大&#xff08;如英文和中文、阿拉伯语等&#xff09;会导致界面布局错位、UI 不美观的问题。 这个问题本质上是 文本…...

01-Redis介绍与安装

01-Redis介绍与安装 SQL与NoSQL SQLNoSQL数据结构结构化非结构化数据关联关联的非关联的查询方式SQL查询非SQL事务特性ACIDBASE存储方式磁盘内存拓展性垂直水平使用场景1、数据结构固定2、相关业务对数据安全性、一致性要求较高1、数据结构不固定2、对安全性、一致性要求不高…...

十六、【前端强化篇】完善 TestCase 编辑器:支持 API 结构化定义与断言配置

【前端强化篇】完善 TestCase 编辑器:支持 API 结构化定义与断言配置 前言准备工作第一步:更新前端 `TestCase` 类型定义第二步:改造 `TestCaseEditView.vue` 表单第三步:修改后端代码中的TestCase模型和序列化器第四步:测试强化后的用例编辑器总结前言 在之前的后端文章…...

Kafka broker 写消息的过程

Producer → Kafka Broker → Replication → Consumer|Partition chosen (by key or round-robin)|Message appended to end of log (commit log)上面的流程是kafka 写操作的大体流程。 kafka 不会特意保留message 在内存中&#xff0c;而是直接写入了disk。 那么消费的时候&…...

VR博物馆推动现代数字化科技博物馆

VR博物馆&#xff1a;推动现代数字化科博馆新篇章 随着科技的飞速发展&#xff0c;虚拟现实&#xff08;Virtual Reality, VR&#xff09;技术已经逐渐渗透到我们生活的方方面面&#xff0c;其中&#xff0c;VR博物馆作为现代数字化科博馆的重要形式之一&#xff0c;以独特的优…...

Python爬虫之数据提取

本章节主要会去学习在爬虫中的如何去解析数据的方法&#xff0c;要学习的内容有&#xff1a; 响应数据的分类结构化数据如何提取非结构化数据如何提取正则表达式的语法以及使用jsonpath解析嵌套层次比较复杂的json数据XPath语法在Python代码中借助lxml模块使用XPath语法提取非…...

第2讲、Odoo深度介绍:开源ERP的领先者

一、Odoo深度介绍&#xff1a;开源ERP的领先者 Odoo&#xff0c;其前身为OpenERP&#xff0c;是一款在全球范围内广受欢迎的开源企业管理软件套件。它不仅仅是一个ERP系统&#xff0c;更是一个集成了客户关系管理&#xff08;CRM&#xff09;、电子商务、网站构建、项目管理、…...

【TCP/IP和OSI模型以及区别——理论汇总】

参考小林code和卡尔哥&#xff0c;感恩&#xff01; 网络基础篇 面试官您好&#xff01;OSI和TCP/IP是网络通信中两个关键模型&#xff0c;本质都是分层处理数据传输&#xff0c;但设计理念和应用场景差异很大。 OSI模型是理论上的七层架构&#xff0c;从下到上依次是物理层…...

【HarmonyOS 5】生活与服务开发实践详解以及服务卡片案例

一、金融场景创新实践 ‌智慧银行网点转型‌ 通过统一设备方案整合国产芯片与鸿蒙系统&#xff0c;支持智能柜员机、移动展业终端等设备的弹性硬件组合&#xff0c;降低25%硬件成本。利用‌元服务框架‌实现卡片式交互&#xff08;如客户画像、风险评估一键调取&#xff09;&a…...

LEAP模型能源需求/供应预测、能源平衡表核算、空气污染物排放预测、碳排放建模预测、成本效益分析、电力系统优化

&#x1f310; LEAP模型&#xff08;Long-range Energy Alternatives Planning System&#xff09;&#xff0c;即长期能源替代规划系统&#xff0c;是由斯德哥尔摩环境研究所与美国波士顿大学共同开发的基于情景分析的自底向上的能源—环境核算工具。该模型采用自底向上的架构…...

STM32 I2C通信外设

1、外设简介 可变多主机 7位/10位寻址 10位寻址&#xff1a;起始之后的两个字节都作为寻址&#xff0c;第一个字节前5位是11110作为10位寻址的标志位 SMBus&#xff1a;系统管理总线&#xff0c;主要用于电源管理&#xff0c;与I2C类似 2、外设结构框图 比较器、自身地址寄…...

13. springCloud AlibabaSeata处理分布式事务

目录 一、分布式事务面试题 1.多个数据库之间如何处理分布式事务&#xff1f; 2.若拿出如下场景&#xff0c;阁下将如何应对? 3.阿里巴巴的Seata-AT模式如何做到对业务的无侵入? 4.对于分布式事务问题&#xff0c;你知道的解决方案有哪些?请你谈谈? 二、分布式事务问题…...

MySQL 表的内连和外连

一、内连接 内连接实际上就是利用 where 子句对两种表形成的笛卡儿积进行筛选&#xff0c;前面学习的查询都是内连接&#xff0c;也是在开发过程中使用的最多的连接查询。 select 字段 from 表1 inner join 表2 on 连接条件 and 其他条件; 注意&#xff1a;前面学习的都是内连…...

VR线上展厅特点分析与优势

VR线上展厅&#xff1a;特点、优势与实际应用 VR线上展厅&#xff0c;作为虚拟现实&#xff08;VR&#xff09;技术在展示行业的创新应用&#xff0c;正逐步改变着传统的展览方式。通过模拟真实的物理环境&#xff0c;为参观者提供身临其境的展览体验&#xff0c;成为展示行业…...

Python基于SVM技术的手写数字识别问题项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档&#xff09;&#xff0c;如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在当今数字化转型加速的时代&#xff0c;手写数字识别作为图像处理与机器学习领域的一个经典问题&#xff0c;具有广…...

Elasticsearch的写入性能优化

优化Elasticsearch的写入性能需要从多维度入手,包括集群配置、索引设计、数据处理流程和硬件资源等。以下是一些关键优化策略和最佳实践: 一、索引配置优化 合理设置分片数与副本数分片数(Shards):过少会导致写入瓶颈(无法并行),过多会增加集群管理开销。公式参考:分…...

2024年数维杯国际大学生数学建模挑战赛A题飞行器激光测速中的频率估计问题解题全过程论文及程序

2024年数维杯国际大学生数学建模挑战赛 A题 复合直升机的建模与优化控制问题 原题再现&#xff1a; &#xff08;一&#xff09; 问题的背景   空速&#xff0c;即飞机相对于空气的速度&#xff0c;是飞行期间需要监控的关键参数。空速与飞行状态密切相关&#xff0c;如迎角…...

AWS 成本异常检测IAM策略

问题 审计人员需要看AWS 成本异常检测&#xff0c;则需要开通这个权限。 IAM 自定义策略 {"Version": "2012-10-17","Statement": [{"Action": ["ce:Get*"],"Effect": "Allow","Resource"…...

解决Vue3+uni-app导航栏高亮自动同步方案

路由跳转自动识别导航高亮实现方法 以下代码使用wd-tabbar组件实现路由跳转时自动同步导航栏高亮状态&#xff0c;适用于所有的Vue3uni-app项目。 请根据自身使用框架类型完成&#xff0c;也可根据我使用的UI组件进行完成地址如下&#xff1a; Tabbar 标签栏 | Wot UI &#…...

DeepSeek+SpringAI实现流式对话

大模型的响应速度通常是很慢的&#xff0c;为了避免用户用户能够耐心等待输出的结果&#xff0c;我们通常会使用流式输出一点点将结果输出给用户。 那么问题来了&#xff0c;想要实现流式结果输出&#xff0c;后端和前端要如何配合&#xff1f;后端要使用什么技术实现流式输出呢…...

【Spark征服之路-2.1-安装部署Spark(一)】

实验目标&#xff1a; 本节课实验将完成Spark 4种部署模式的其中2种&#xff0c;分别是Local、Standalone模式。 实验准备工作&#xff1a; 三台linux虚拟机spark的压缩包 实验步骤&#xff1a; Spark-local Spark的Local模式仅需要单个虚拟机节点即可&#xff0c;无需启…...

VS代码生成工具ReSharper v2025.1——支持.NET 10和C# 14预览功能

实质上&#xff0c;ReSharper特征可用于C#&#xff0c;VB.net&#xff0c;XML&#xff0c;Asp.net&#xff0c;XAML和构建脚本。 使用ReSharper&#xff0c;你可以进行深度代码分析&#xff0c;智能代码协助&#xff0c;实时错误代码高亮显示&#xff0c;解决方案范围内代码分析…...

【Godot】如何导出 Release 版本的安卓项目

在使用 Godot 引擎开发安卓游戏或应用时&#xff0c;发布到应用市场&#xff08;如 Google Play、华为应用市场等&#xff09;通常需要生成一个 Release 版本的 .apk 包&#xff0c;而非 Debug 版本。本文将详细介绍如何将 Godot 项目导出为 Release 版本的安卓项目&#xff0c…...

VSCode 工作区配置文件通用模板(CMake + Ninja + MinGW/GCC 编译器 的 C++ 或 Qt 项目)

下面是一个通用模板&#xff0c;适用于大多数使用 VSCode CMake Ninja MinGW/GCC 编译器 的 C 或 Qt 项目。你可以将这个 .vscode 文件夹复制到你的项目根目录下&#xff0c;稍作路径调整即可使用。 &#x1f4c1; .vscode/ 目录结构&#xff08;通用模板&#xff09; .vs…...

js鼠标事件大全

一、鼠标相关事件&#xff08;Mouse Events&#xff09; 事件名描述支持浏览器&#xff08;HTML 版本&#xff09;onClick鼠标单击对象时触发IE3, N2, O3onDblClick鼠标双击对象时触发IE4, N4, OonMouseDown鼠标按键按下时触发IE4, N4, OonMouseUp鼠标按键释放时触发IE4, N4, …...

Java八股文——Redis篇

目录 1. 缓存穿透解决方案1. 缓存空值2. 布隆过滤器&#xff08;Bloom Filter&#xff09;3. 参数校验4. 接口限流与验证码 2. 缓存击穿解决方案1. 设置热点数据永不过期&#xff08;或很长过期时间&#xff09;2. 使用互斥锁&#xff08;如分布式锁&#xff09;3. 利用异步更新…...