数据结构-堆和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. 需求分析与规划 目标定义:明确模型用途(如对话、翻译、代码生成…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...
前端中slice和splic的区别
1. slice slice 用于从数组中提取一部分元素,返回一个新的数组。 特点: 不修改原数组:slice 不会改变原数组,而是返回一个新的数组。提取数组的部分:slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...

Unity中的transform.up
2025年6月8日,周日下午 在Unity中,transform.up是Transform组件的一个属性,表示游戏对象在世界空间中的“上”方向(Y轴正方向),且会随对象旋转动态变化。以下是关键点解析: 基本定义 transfor…...

篇章二 论坛系统——系统设计
目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…...

yaml读取写入常见错误 (‘cannot represent an object‘, 117)
错误一:yaml.representer.RepresenterError: (‘cannot represent an object’, 117) 出现这个问题一直没找到原因,后面把yaml.safe_dump直接替换成yaml.dump,确实能保存,但出现乱码: 放弃yaml.dump,又切…...
JS红宝书笔记 - 3.3 变量
要定义变量,可以使用var操作符,后跟变量名 ES实现变量初始化,因此可以同时定义变量并设置它的值 使用var操作符定义的变量会成为包含它的函数的局部变量。 在函数内定义变量时省略var操作符,可以创建一个全局变量 如果需要定义…...