数据结构-堆和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…...
【玩转 Postman 接口测试与开发2_017】第13章:在 Postman 中实现契约测试(Contract Testing)与 API 接口验证(下)
《API Testing and Development with Postman》最新第二版封面 文章目录 第十三章 契约测试与 API 接口验证8 导入官方契约测试集合9 契约测试集合的详细配置9.1 env-apiKey 的创建与设置9.2 env-workspaceId 的设置9.3 Mock 服务器及 env-server 的配置9.4 API 测试实例的配置…...
R语言 | 使用 ComplexHeatmap 绘制热图,分区并给对角线分区加黑边框
目的:画热图,分区,给对角线分区添加黑色边框 建议直接看0和4。 0. 准备数据 # 安装并加载必要的包 #install.packages("ComplexHeatmap") # 如果尚未安装 library(ComplexHeatmap)# 使用 iris 数据集 #data(iris)# 选择数值列&a…...
React图标库: 使用React Icons实现定制化图标效果
React图标库: 使用React Icons实现定制化图标效果 图标库介绍 是一个专门为React应用设计的图标库,它包含了丰富的图标集合,覆盖了常用的图标类型,如FontAwesome、Material Design等。React Icons可以让开发者在React应用中轻松地添加、定制各…...
Python sider-ai-api库 — 访问Claude、llama、ChatGPT、gemini、o1等大模型API
目前国内少有调用ChatGPT、Claude、Gemini等国外大模型API的库。 Python库sider_ai_api 提供了调用这些大模型的一个完整解决方案, 使得开发者能调用 sider.ai 的API,实现大模型的访问。 Sider是谷歌浏览器和Edge的插件,能调用ChatGPT、Clau…...
DeepSeek、哪吒和数据库:厚积薄发的力量
以下有部分来源于AI,毕竟我认为AI还不能替代,他只能是辅助 快速迭代是应用程序不是工程 在这个追求快速迭代、小步快跑的时代,我们似乎总是被 “快” 的节奏裹挟着前进。但当我们静下心来,审视 DeepSeek 的发展、饺子导演创作哪吒…...
DDD - 微服务架构模型_领域驱动设计(DDD)分层架构 vs 整洁架构(洋葱架构) vs 六边形架构(端口-适配器架构)
文章目录 引言1. 概述2. 领域驱动设计(DDD)分层架构模型2.1 DDD的核心概念2.2 DDD架构分层解析 3. 整洁架构:洋葱架构与依赖倒置3.1 整洁架构的核心思想3.2 整洁架构的层次结构 4. 六边形架构:解耦核心业务与外部系统4.1 六边形架…...
第 1 天:UE5 C++ 开发环境搭建,全流程指南
🎯 目标:搭建 Unreal Engine 5(UE5)C 开发环境,配置 Visual Studio 并成功运行 C 代码! 1️⃣ Unreal Engine 5 安装 🔹 下载与安装 Unreal Engine 5 步骤: 注册并安装 Epic Game…...
【华为OD-E卷 - 109 磁盘容量排序 100分(python、java、c++、js、c)】
【华为OD-E卷 - 磁盘容量排序 100分(python、java、c、js、c)】 题目 磁盘的容量单位常用的有M,G,T这三个等级, 它们之间的换算关系为1T 1024G,1G 1024M, 现在给定n块磁盘的容量,…...
【大数据技术】编写Python代码实现词频统计(python+hadoop+mapreduce+yarn)
编写Python代码实现词频统计(python+hadoop+mapreduce+yarn) 搭建完全分布式高可用大数据集群(VMware+CentOS+FinalShell) 搭建完全分布式高可用大数据集群(Hadoop+MapReduce+Yarn) 本机PyCharm连接CentOS虚拟机 在阅读本文前,请确保已经阅读过以上三篇文章,成功搭建了…...
5-Scene层级关系
Fiber里有个scene是只读属性,能从fiber中获取它属于哪个场景,scene实体中又声明了fiber,fiber与scene是互相引用的关系。 scene层级关系 举例 在unity.core中的EntityHelper中,可以通过entity获取对应的scene root fiber等属性…...
JVM执行流程与架构(对应不同版本JDK)
直接上图(对应JDK8以及以后的HotSpot) 这里主要区分说明一下 方法区于 字符串常量池 的位置更迭: 方法区 JDK7 以及之前的版本将方法区存放在堆区域中的 永久代空间,堆的大小由虚拟机参数来控制。 JDK8 以及之后的版本将方法…...
本地部署 DeepSeek-R1:简单易上手,AI 随时可用!
🎯 先看看本地部署的运行效果 为了测试本地部署的 DeepSeek-R1 是否真的够强,我们随便问了一道经典的“鸡兔同笼”问题,考察它的推理能力。 📌 问题示例: 笼子里有鸡和兔,总共有 35 只头,94 只…...
请求响应(接上篇)
请求 日期参数 需要在前面加上一个注解DateTimeFormat来接收传入的参数的值 Json参数 JSON参数:JSON数据键名与形参对象属性名相同,定义POJO类型形参即可接收参数,需要使用 RequestBody 标识 通过RequestBody将JSON格式的数据封装到实体类…...
数组排序算法
数组排序算法 用C语言实现的数组排序算法。 排序算法平均时间复杂度最坏时间复杂度最好时间复杂度空间复杂度是否稳定适用场景QuickO(n log n)O(n)O(n log n)O(log n)不稳定大规模数据,通用排序BubbleO(n)O(n)O(n)O(1)稳定小规模数据,教学用途InsertO(n)…...
防火墙的安全策略
1.VLAN 2属于办公区;VLAN 3属于生产区,创建时间段 [FW]ip address-set BG type object [FW-object-address-set-BG]address 192.168.1.0 mask 25 [FW]ip address-set SC type object [FW-object-address-set-SC]address 192.168.1.129 mask 25 [FW]ip address-se…...
2025Java面试题超详细整理《微服务篇》
什么是微服务架构? 微服务框架是将某个应用程序开发划分为许多独立小型服务,实现敏捷开发和部署,这些服务一般围绕业务规则进行构建,可以用不同的语言开发,使用不同的数据存储,最终使得每个服务运行在自己…...
中位数定理:小试牛刀> _ <2025牛客寒假1
给定数轴上的n个点,找出一个到它们的距离之和尽量小的点(即使我们可以选择不是这些点里的点,我们还是选择中位数的那个点最优) 结论:这些点的中位数就是目标点。可以自己枚举推导(很好想) (对于 点的数量为…...
(2025,LLM,下一 token 预测,扩散微调,L2D,推理增强,可扩展计算)从大语言模型到扩散微调
Large Language Models to Diffusion Finetuning 目录 1. 概述 2. 研究背景 3. 方法 3.1 用于 LM 微调的高斯扩散 3.2 架构 4. 主要实验结果 5. 结论 1. 概述 本文提出了一种新的微调方法——LM to Diffusion (L2D),旨在赋予预训练的大语言模型(…...
如何开发一个大语言模型,开发流程及需要的专业知识
开发大型语言模型(LLM)是一个复杂且资源密集的过程,涉及多个阶段和跨学科知识。以下是详细的开发流程和所需专业知识指南: 一、开发流程 1. 需求分析与规划 目标定义:明确模型用途(如对话、翻译、代码生成…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
【 java 虚拟机知识 第一篇 】
目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...
android13 app的触摸问题定位分析流程
一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...
给网站添加live2d看板娘
给网站添加live2d看板娘 参考文献: stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下,文章也主…...
微服务通信安全:深入解析mTLS的原理与实践
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、引言:微服务时代的通信安全挑战 随着云原生和微服务架构的普及,服务间的通信安全成为系统设计的核心议题。传统的单体架构中&…...
软件工程 期末复习
瀑布模型:计划 螺旋模型:风险低 原型模型: 用户反馈 喷泉模型:代码复用 高内聚 低耦合:模块内部功能紧密 模块之间依赖程度小 高内聚:指的是一个模块内部的功能应该紧密相关。换句话说,一个模块应当只实现单一的功能…...
恶补电源:1.电桥
一、元器件的选择 搜索并选择电桥,再multisim中选择FWB,就有各种型号的电桥: 电桥是用来干嘛的呢? 它是一个由四个二极管搭成的“桥梁”形状的电路,用来把交流电(AC)变成直流电(DC)。…...
macOS 终端智能代理检测
🧠 终端智能代理检测:自动判断是否需要设置代理访问 GitHub 在开发中,使用 GitHub 是非常常见的需求。但有时候我们会发现某些命令失败、插件无法更新,例如: fatal: unable to access https://github.com/ohmyzsh/oh…...
跨平台商品数据接口的标准化与规范化发展路径:淘宝京东拼多多的最新实践
在电商行业蓬勃发展的当下,多平台运营已成为众多商家的必然选择。然而,不同电商平台在商品数据接口方面存在差异,导致商家在跨平台运营时面临诸多挑战,如数据对接困难、运营效率低下、用户体验不一致等。跨平台商品数据接口的标准…...
