数据结构-堆和PriorityQueue
1.堆(Heap)
1.1堆的概念
堆是一种非常重要的数据结构,通常被实现为一种特殊的完全二叉树
如果有一个关键码的集合K={k0,k1,k2,...,kn-1},把它所有的元素按照完全二叉树的顺序存储在一个一维数组中,如果满足ki<=k2i+1且ki<=k2i+2(i=0,1,2,3...),则称这个集合为小堆;如果满足ki>=k2i+1且ki>=k2i+2(i=0,1,2,3...),则称这个集合为大堆。
简单来说,根节点的值为最大的堆叫做最大堆或大根堆,根节点的值最小的堆叫做最小堆或小根堆

1.2堆的性质
1.完全二叉树的性质:
除了最后一层外,每一层都被填满,最后一层的节点从左往后填充
2.堆序性质:
最大堆(大根堆):
对于每个节点i,其左子节点2i+1和右子节点2i+2的值都小于或等于i的值
最小堆(小根堆):
对于每个节点i,其左子节点2i+1和右子节点2i+2的值都大于或等于i的值
1.3堆的存储方式
从堆的概念可知,堆是一颗完全二叉树,因此可以以层序的规则方式来高效存储

注意: 对于非完全二叉树来说,不适合使用顺序的方式进行存储,因为为了还原二叉树,空间中必须要存储空节点,就会导致空间利用率比较低
1.4堆的创建
给定一个集合{28,16,20,19,29,35,66,50,26,38},如何将其创建为堆呢?

观察发现:根节点的左右子树都已经满足堆的性质,因此只需要将根节点向下调整即可
1.4.1堆的向下调整
1.用parent表示要调整的节点,child表示parent的左孩子(注意:堆是一颗完全二叉树,如果parent有孩子一定是先有左孩子
2.如果parent的左孩子存在,即child<size,进行如下操作,直到parent的左孩子不存在:
parent的右孩子是否存在,如果存在,则找到左右孩子中最小的元素,让child表示这个元素。
将parent与较小的孩子child进行比较,如果parent小于child,调整结束。
如果parent大于child,将parent和child进行交换,原来parent中较大的元素向下调整可能会导 致子树不满足堆的性质,因此要继续向下调整,即parent=child,child=2*parent+1,继续进 行步骤2

代码编写:
public void shiftDown(int[] array,int parent){int child=2*parent+1;int size=array.length;while(child<size){//如果右孩子存在,用child标记左右孩子中较小的值if(child+1<size&&array[child+1]<array[child]){child++;}if(array[parent]>array[child]){swap(array,parent,child);//继续向下调整,为了保证子树也满足堆的性质parent=child;child=2*parent+1;}else{break;}}}private void swap(int[] array,int a,int b){int tmp=array[a];array[a]=array[b];array[b]=tmp;}
注意:再调整以parent为根的二叉树时,必须满足parent的左子树和右子树已经时堆了才可以进行向下调整。
1.4.2堆的创建(小根堆)
那么对于普通的序列,即根节点的左右子树不满足堆的性质,又该如何创建呢?
例如对普通序列{2,7,8,5,4,1}进行小根堆的创建
根据上面的堆的向下调整,我们的思路就是要将根的左右子树都满足小根堆的特点,我们可以从下向上,从最后一个非叶子节点出发,将其与他们的左右孩子进行比较,将最小的值与非叶子节点进行交换(堆的向下调整),再继续向上执行上述操作,直到操作的节点为根节点即可

代码编写:
public void createSmallestHeap(int [] array){int root=(array.length-2)>>1;for(;root>=0;root--){shiftDown(array,root);}}
1.5堆的插入和删除
1.5.1堆的插入
堆的插入总共需要2步:
1.先将元素放入底层(空间不够时,需要进行扩容)
2.将新插入的元素不断向上调整,直到满足堆的性质

观察可以发现:如果新插入的节点的父节点大于新插入的节点,就进行元素的交换,不断重复该动作
代码编写:
//child表示新插入元素的索引public void shiftUp(int child){//找到新插入节点的父节点int parent=(child-1)/2;while(child>0){if(array[parent]>array[child]){swap(array,parent,child);child=parent;parent=(child-1)/2;}else {break;}}}
1.5.2堆的删除
堆在删除过程中需要注意删除的元素一定是堆顶元素
1.将堆顶元素和堆中的左后一个元素交换位置
2.将堆中有效个数减少一个
3.对堆顶元素进行向下调整

代码编写:
public void shiftDown(int[] array,int parent){int child=2*parent+1;int size=array.length;while(child<size){//如果右孩子存在,用child标记左右孩子中较小的值if(child+1<size&&array[child+1]<array[child]){child++;}if(array[parent]>array[child]){swap(array,parent,child);//继续向下调整,为了保证子树也满足堆的性质parent=child;child=2*parent+1;}else{break;}}}public void delete(int[] array){swap(array,0,size-1);//size表示有效元素的个数size--;shiftDown(array,0);}
1.6堆的应用
1.堆排序(Heap Sort)
利用堆的性质对数组进行排序,时间复杂度为O(nlogn)
2.优先级队列(PriorityQueue)
堆是实现优先级队列的高校数据结构,支持快速的插入和删除操作
3.Dijkstra算法
在最短路径算法中,堆用于高效地选择当前距离最小的节点
4.Kth Largest Element
也叫topK问题,使用堆可以高效地找到数组中的第k大元素
2.PriorityQueue
2.1什么是优先级队列
通过之前的介绍,队列是一种先进先出(FIFO)的数据结构,但是优先情况下,操作的数据可能带有优先级,并不希望按照队列原始的顺序进行出栈,可能优先级高的元素想先出队列
在生活中有一个很常见的例子:当你在用听歌的时候,突然接到电话,音乐会自动停止,而执行通话的操作
优先级队列(PriorityQueue)是一种特殊的队列,其中的每个元素都有一个优先级,队列会根据优先级来决定元素的出队顺序,优先级高的元素先出队,优先级低的元素后出队,如果两个元素的优先级相同,则按照它们入队列的顺序出队
优先级队列通常基于堆这种数据结构实现,因为堆可以高效地进行插入和删除操作,同时保持元素的优先级顺序
Java集合框架中提供了PriorityQueue和PriorityBlockingQueue两种类型的优先级队列,PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全的,我们主要进行讲解PriorityQueue
2.2PriorityQueue常见操作
PriorityQueue的常见方法:
| 方法 | 解释 |
| PriorityQueue() | 创建一个空的优先级队列,默认容量是11 |
| PriorityQueue(int initialCapacity) | 创建一个初始容量为initialCapacity的优先级队列,注意:initialCapacity不能小于1,否则会抛出IllegalArgumentException异常 |
| PriorityQueue(Collection<? extends E> c) | 用一个集合来创建优先级队列 |
| boolean offer(E e) | 插入元素e,插入成功返回true,如果e对象为空,则会抛出NullPointerException异常,空间不够的时候会进行扩容 |
| E peek() | 获取优先级最高的元素,如果优先级队列为空,返回null |
| E poll() | 移除优先级最高的元素,如果优先级队列为空,返回null |
| int size() | 获取有效元素的个数 |
| void clear() | 清空队列 |
| boolean isEmpty() | 检测优先级队列是否为空,如果为空返回true |
我们以一个复杂的类型来演示:
public class Student implements Comparable<Student>{private String name;private int age;public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic int compareTo(Student o) {return this.age-o.age;}
}public class Test {public static void main(String[] args) {PriorityQueue<Student> queue=new PriorityQueue<>();Student s1=new Student("hajimi",21);Student s2=new Student("king",18);queue.offer(s1);queue.offer(s2);System.out.println(queue.size());for(Student s:queue){System.out.println(s);}queue.clear();System.out.println(queue.size());}
}
2.3优先级队列的特性
因为优先级队列是基于堆实现的,所以优先级队列的操作的时间复杂度其实就是堆在操作过程中的时间复杂度
1.插入:
将一个新元素插入到队列中,并根据优先级调整队列的顺序,时间复杂度为O(logn)
2.删除:
删除优先级最高的元素,时间复杂度为O(logn)
3.获取优先级最高的元素:
返回优先级最高的元素,但不删除它,时间复杂度为O(1)
4.更新优先级:
更新队列中某个元素的优先级,并调整队列的顺序,时间复杂度为O(logn)
5.Priority中放置的元素必须是能够比较大小的,不能插入无法比较大小的对象,否则会抛出ClassCastException异常
6.不能插入null,否则会抛出NullPointerException
7.PriorityQueue默认情况下是小堆
8.PriorityQueue其内部可以自动扩容
2.4PriorityQueue底层的扩容原理


PriorityQueue的 默认无参构造方法创建的数组长度为11

如果容量小于64时,是按照oldCapacity的2倍方式扩容的
如果容量大于64时,是按照oldCapacity的1.5倍方式扩容的
如果容量超过MAX_ARRAY_SIZE,按照MAX_ARRAY_SIZE来进行扩容
2.5实现一个优先级队列
package datastructure;import java.util.Arrays;public class PriorityQueue {private int elem[];//队列中有效元素的个数private int usedSize;private final static int DEFAULT_INIT_SIZE=11;public PriorityQueue(){elem=new int[DEFAULT_INIT_SIZE];}public PriorityQueue(int[] elem){this.elem=elem;this.usedSize=elem.length;int root=((elem.length-2)>>1);for(;root>=0;root--){shiftDown(root,elem.length);}}private void shiftDown(int parent,int len){int child=parent*2+1;while (child<len){if(child+1<len&&elem[child+1]<elem[child]){child++;}if(elem[parent]>elem[child]){swap(elem,parent,child);}else {break;}}}public boolean offer(int value){if(usedSize==elem.length){Arrays.copyOf(elem,2*elem.length);}elem[usedSize]=value;usedSize++;shiftUp(usedSize-1);return true;}private void swap(int[] elem,int a,int b){int tmp=elem[a];elem[a]=elem[b];elem[b]=tmp;}private void shiftUp(int child){int parent=(child-1)/2;while (child>0){if(elem[child]<elem[parent]){swap(elem,parent,child);child=parent;parent=(child-1)/2;}else {break;}}}public int size(){return usedSize;}public int peek(){if(usedSize==0){System.out.println("优先级队列中没有元素,无法获取元素");return -1;}return elem[0];}public boolean isEmpty(){return usedSize==0;}public int poll(){if(usedSize==0){System.out.println("优先级队列中没有元素,无法删除元素");return -1;}int value=elem[0];swap(elem,0,usedSize-1);usedSize--;shiftDown(0,usedSize-1);return value;}
}
对编写的代码进行运行测试:
public class Test {public static void main(String[] args) {PriorityQueue queue=new PriorityQueue();queue.offer(2);queue.offer(4);queue.offer(3);queue.offer(8);queue.offer(7);queue.offer(5);queue.offer(1);System.out.println(queue.peek());int a= queue.poll();System.out.println(a);System.out.println(queue.peek());System.out.println(queue.size());}
}
相关文章:
数据结构-堆和PriorityQueue
1.堆(Heap) 1.1堆的概念 堆是一种非常重要的数据结构,通常被实现为一种特殊的完全二叉树 如果有一个关键码的集合K{k0,k1,k2,...,kn-1},把它所有的元素按照完全二叉树的顺序存储在一个一维数组中,如果满足ki<k2i…...
如何打造一个更友好的网站结构?
在SEO优化中,网站的结构往往被忽略,但它其实是决定谷歌爬虫抓取效率的关键因素之一。一个清晰、逻辑合理的网站结构,不仅能让用户更方便地找到他们需要的信息,还能提升搜索引擎的抓取效率 理想的网站结构应该像一棵树,…...
每日Attention学习20——Group Shuffle Attention
模块出处 [MICCAI 24] [link] LB-UNet: A Lightweight Boundary-Assisted UNet for Skin Lesion Segmentation 模块名称 Group Shuffle Attention (GSA) 模块作用 轻量特征学习 模块结构 模块特点 使用分组(Group)卷积降低计算量引入External Attention机制更好的学习特征S…...
VUE之组件通信(二)
1、v-model v-model的底层原理:是:value值和input事件的结合 $event到底是啥?啥时候能.target 对于原生事件,$event就是事件对象 ,能.target对应自定义事件,$event就是触发事件时,所传递的数据ÿ…...
[x86 ubuntu22.04]进入S4失败
目录 1 问题描述 2 解决过程 2.1 查看内核日志 2.2 新建一个交换分区 2.3 指定交换分区的位置 1 问题描述 CPU:G6900E OS:ubuntu22.04 Kernel:6.8.0-49-generic 使用“echo disk > /sys/power/state”命令进入 S4,但是无法…...
idea隐藏无关文件
idea隐藏无关文件 如果你想隐藏某些特定类型的文件(例如 .log 文件或 .tmp 文件),可以通过以下步骤设置: 打开设置 在菜单栏中选择 File > Settings(Windows/Linux)或 IntelliJ IDEA > Preference…...
ES6 对象扩展:对象简写,对象属性 表达式,扩展运算符 ...,Object.assign,Object.is,用法和应用场景
1. 对象属性简写 1.1 基本语法 // 传统写法 const name John; const age 25; const user {name: name,age: age };// ES6 简写语法 const user {name,age };1.2 实际应用场景 // 1. 函数返回对象 function createUser(name, age, email) {return {name,age,email}; }// …...
文献阅读 250205-Global patterns and drivers of tropical aboveground carbon changes
Global patterns and drivers of tropical aboveground carbon changes 来自 <Global patterns and drivers of tropical aboveground carbon changes | Nature Climate Change> 热带地上碳变化的全球模式和驱动因素 ## Abstract: Tropical terrestrial ecosystems play …...
C++SLT(三)——list
目录 一、list的介绍二、list的使用list的定义方式 三、list的插入和删除push_back和pop_backpush_front和pop_frontinserterase 四、list的迭代器使用五、list的元素获取六、list的大小控制七、list的操作函数sort和reversemergeremoveremove_ifuniqueassignswap 一、list的介…...
【数据结构】循环链表
循环链表 单链表局限性单向循环链表判断链表是否有环思路code 找到链表入口思路代码结构与逻辑 code 单链表局限性 单链表作为一种基本的数据结构,虽然在很多场景下都非常有用,但它也存在一些局限性: 单向访问:由于每个节点仅包含…...
ImGui 学习笔记(二)—— 多视口
在计算机图形学中,视口(Viewport)是一个可观察的多边形区域。 将物体渲染至图像的过程中,会用两种区域表示。世界坐标窗口是用户所关注的区域(即用户想要可视化的东西),坐标系由应用程序确定。…...
vue2-mixin的定义与和使用
文章目录 1. 什么是mixin2. 局部混入3. 全局混入4. 多mixin混入冲突4.1 替换性4.2 合并型4.3 合并队列型4.4 叠加性 5. 使用场景 #vue2-mixin的使用 1. 什么是mixin Mixin是面向对象语言中的一个类,提供了方法的实现,其他类可以访问mixin类的方法而不用…...
安装和卸载RabbitMQ
我的飞书:https://rvg7rs2jk1g.feishu.cn/docx/SUWXdDb0UoCV86xP6b3c7qtMn6b 使用Ubuntu环境进行安装 一、安装Erlang 在安装RabbitMQ之前,我们需要先安装Erlang,RabbitMQ需要Erlang的语言支持 #安装Erlang sudo apt-get install erlang 在安装的过程中,会弹出一段信息,此…...
Apache HttpClient
HttpClient是apache组织下面的一个用于处理HTTP请求和响应的来源工具,是一个在JDK基础类库是做了更好的封装的类库。 HttpClient 使用了连接池技术来管理 TCP 连接,这有助于提高性能并减少资源消耗。连接池允许 HttpClient 复用已经建立的连接࿰…...
Golang 并发机制-6:掌握优雅的错误处理艺术
并发编程可能是提高软件系统效率和响应能力的一种强有力的技术。它允许多个工作负载同时运行,充分利用现代多核cpu。然而,巨大的能力带来巨大的责任,良好的错误管理是并发编程的主要任务之一。 并发代码的复杂性 并发编程增加了顺序程序所不…...
react使用DatePicker日期选择器
1、引入:npm i day 2、页面引入: import dayjs from dayjs; 3、使用 <DatePicker onChange{onChange} value{datas ? dayjs(datas) : null} /> 4、事件 const onChange (date, dateString) > {setInput(dateString)setDatas(dateString)…...
第27节课:安全审计与防御—构建坚固的网络安全防线
目录 安全审计工具与流程安全审计工具NessusNmapBurp Suite 安全审计流程规划与准备信息收集漏洞扫描分析与评估报告与建议 安全防御策略网络层防御应用层防御数据层防御安全管理 结语 在当今数字化时代,网络安全已成为企业和个人不可忽视的重要议题。随着网络攻击手…...
深度学习 Pytorch 基础网络手动搭建与快速实现
为了方便后续练习的展开,我们尝试自己创建一个数据生成器,用于自主生成一些符合某些条件、具备某些特性的数据集。 导入相关的包 # 随机模块 import random# 绘图模块 import matplotlib as mpl import matplotlib.pyplot as plt# 导入numpy import nu…...
保姆级教程Docker部署KRaft模式的Kafka官方镜像
目录 一、安装Docker及可视化工具 二、单节点部署 1、创建挂载目录 2、运行Kafka容器 3、Compose运行Kafka容器 4、查看Kafka运行状态 三、集群部署 四、部署可视化工具 1、创建挂载目录 2、运行Kafka-ui容器 3、Compose运行Kafka-ui容器 4、查看Kafka-ui运行状态 …...
51单片机看门狗系统
在 STC89C52 单片机中,看门狗控制寄存器的固定地址为 0xE1。此地址由芯片厂商在硬件设计时确定,但是它在头文件中并未给出,因此在使用看门狗系统时需要声明下这个特殊功能寄存器 sfr WDT_CONTR 0xE1; 本案将用一个小灯的工作状况来展示看门…...
RNN/LSTM/GRU 学习笔记
文章目录 RNN/LSTM/GRU一、RNN1、为何引入RNN?2、RNN的基本结构3、各种形式的RNN及其应用4、RNN的缺陷5、如何应对RNN的缺陷?6、BPTT和BP的区别 二、LSTM1、LSTM 简介2、LSTM如何缓解梯度消失与梯度爆炸? 三、GRU四、参考文献 RNN/LSTM/GRU …...
Android记事本App设计开发项目实战教程2025最新版Android Studio
平时上课录了个视频,从新建工程到打包Apk,从头做到尾,没有遗漏任何实现细节,欢迎学过Android基础的同学参加,如果你做过其他终端软件开发,也可以学习,快速上手Android基础开发。 Android记事本课…...
【Leetcode 每日一题 - 补卡】680. 验证回文串 II
问题背景 给你一个字符串 s s s,最多 可以从中删除一个字符。 请你判断 s s s 是否能成为回文字符串:如果能,返回 t r u e true true;否则,返回 f a l s e false false。 数据约束 1 ≤ s . l e n g t h ≤ 1 0 …...
Leetcode 8283 移除排序链表中的重复元素
Leetcode 82&83 移除排序链表中的重复元素 Leetcode 83 题目描述 给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。 示例 1: 输入:head [1,1,2] 输出:[1,2] 示…...
【R语言】获取数据
R语言自带2种数据存储格式:*.RData和*.rds。 这两者的区别是:前者既可以存储数据,也可以存储当前工作空间中的所有变量,属于非标准化存储;后者仅用于存储单个R对象,且存储时可以创建标准化档案,…...
为什么在springboot中使用autowired的时候它黄色警告说不建议使用字段注入
byType找到多种实现类导致报错 Autowired: 通过byType 方式进行装配, 找不到或是找到多个,都会抛出异常 我们在单元测试中无法进行字段注入 字段注入通常是 private 修饰的,Spring 容器通过反射为这些字段注入依赖。然而,在单元测试中&…...
Unity游戏(Assault空对地打击)开发(6) 鼠标光标的隐藏
前言 鼠标光标在游戏界面太碍眼了,要隐藏掉。 详细操作 新建一个脚本HideCursor,用于隐藏/取消隐藏光标。 写入以下代码。 意义:游戏开始自动隐藏光标,按Esc(隐藏<-->显示)。 using System.Collectio…...
哪些专业跟FPGA有关?
FPGA产业作为近几年新兴的技术领域,薪资高、待遇好,吸引了大量的求职者。特别是对于毕业生,FPGA领域的岗位需求供不应求。那么,哪些专业和FPGA相关呢? 哪些专业跟FPGA有关? 微电子学与固体电子学、微电子科…...
UE5 蓝图学习计划 - Day 14:搭建基础游戏场景
在上一节中,我们 确定了游戏类型,并完成了 项目搭建、角色蓝图的基础设置(移动)。今天,我们将进一步完善 游戏场景,搭建 地形、墙壁、机关、触发器 等基础元素,并添加角色跳跃功能,为…...
ZooKeeper单节点详细部署流程
ZooKeeper单节点详细部署流程 文章目录 ZooKeeper单节点详细部署流程 一.下载稳定版本**ZooKeeper**二进制安装包二.安装并启动**ZooKeeper**1.安装**ZooKeeper**2.配置并启动**ZooKeeper** ZooKeeper 版本与 JDK 兼容性3.检查启动状态4.配置环境变量 三.可视化工具管理**Zooke…...
