模拟实现优先级队列

目录

定义

特点

构造函数

常用方法

关于扩容的问题

关于建堆的问题

向上调整和向下调整的比较

(向上调整)代码 

(向下调整)代码 

关于入队列和出队列问题

模拟实现优先级队列代码

关于堆排序的问题

堆排序代码

关于对象的比较

基于Comparable的比较

基于Comparator的比较


定义

在Java中,PriorityQueue 是一个基于优先级堆的无界优先级队列。优先级队列的元素按照其自然顺序进行排序,或者根据构造队列时所提供的 Comparator 进行排序,具体取决于所使用的构造器。PriorityQueue 不允许 null 元素。

PriorityQueue默认是小根堆。 

特点

1.无界:PriorityQueue 可以根据需要动态增长以容纳任意数量的元素。
2.基于优先级:队列中的元素按照优先级进行排序。元素的优先级可以是其自然顺序(对于实现了 Comparable 接口的元素),或者根据构造时提供的 Comparator 来确定。
3.不允许 null 元素:尝试添加 null 元素到 PriorityQueue 会抛出 NullPointerException。
4.非线程安全:如果多个线程同时访问一个 PriorityQueue 实例,并且至少有一个线程从结构上修改了队列,那么它必须保持外部同步。

为什么PriorityQueue不能够插入null对象?下面我们看一下源码:

构造函数

1.PriorityQueue():创建一个默认初始容量的 PriorityQueue,其元素将按照其自然顺序进行排序。
2.PriorityQueue(int initialCapacity):创建一个具有指定初始容量的 PriorityQueue,其元素将按照其自然顺序进行排序。
3.PriorityQueue(Collection<? extends E> c):创建一个包含指定集合元素的 PriorityQueue,其元素将按照其自然顺序进行排序。
4.PriorityQueue(int initialCapacity, Comparator<? super E> comparator):创建一个具有指定初始容量的 PriorityQueue,并根据指定的比较器对其元素进行排序。
5.PriorityQueue(Collection<? extends E> c, Comparator<? super E> comparator):创建一个包含指定集合元素的 PriorityQueue,并根据指定的比较器对其元素进行排序。

常用方法

1.boolean add(E e):将指定元素插入此优先级队列。
2.E poll():检索并移除此队列的头,即在此队列中优先级最低/最高的元素。如果此队列为空,则返回 null。
3.E peek():检索但不移除此队列的头;如果此队列为空,则返回 null。
4.int size():返回队列中的元素数量。
5.boolean isEmpty():如果此队列不包含任何元素,则返回 true。
6.void clear():移除此队列中的所有元素。 

关于扩容的问题

通过源码我们了解到,当容量小于64的时候,每次扩容只增加两个空间;当容量大于64的时候,每次扩容是1.5倍扩容。

并且,定义了一个常量值MAX_ARRAY_SIZE=MAX_VALUE-8,当计算后得到的新容量溢出时会抛异常OutOfMemoryError;当计算后得到的新容量大于MAX_ARRAY_SIZE时,新容量设置为MAX_VALUE。

关于建堆的问题

因为优先级队列是基于堆实现的,因此要想实现优先级队列,必须先实现建堆。

那么建立大根堆还是小根堆呢?需要根据实际需要来决定建立大根堆还是小根堆。

向上调整和向下调整的比较

那么建堆的时候使用“向上调整算法”还是“向下调整算法”呢?

下面我们分析一下二者的时间复杂度:

向下调整算法:时间复杂度为N

向上调整算法:时间复杂度为N*logN

显然,使用“向下调整”比“向上调整”更好。

(向上调整)代码 
    private void shiftUp(int child) {int parent = (child-1)/2;while (child > 0) {if (elem[child] > elem[parent]) {swap(child,parent);child = parent;parent = (child-1)/2;} else {break;}}}private void swap(int i,int j) {int tmp = elem[i];elem[i] = elem[j];elem[j] = tmp;}
(向下调整)代码 

    private void shiftDown(int parent, int usedSize) {int child = (2*parent)+1;//左孩子下标while (child < usedSize) {if(child+1 < usedSize && elem[child] < elem[child+1]) {child++;}//child一定是 左右孩子最大值的下标if(elem[child] > elem[parent]) {swap(child, parent);parent = child;child = 2*parent+1;}else {//已经是大根堆了break;}}}private void swap(int i,int j) {int tmp = elem[i];elem[i] = elem[j];elem[j] = tmp;}
关于入队列和出队列问题

策略:

针对入队列:将要插入的元素放到堆尾,然后对该位置进行“向上调整”即可;

针对出队列:将要删除的元素(即堆顶元素)和堆尾元素进行交换,然后把队列的大小-1,然后从堆顶进行“向下调整”即可。

模拟实现优先级队列代码
public class TestHeap {private int[] elem;private int usedSize;public TestHeap() {this.elem = new int[10];}public void initHeap(int[] array) {for (int i = 0; i < array.length; i++) {elem[i] = array[i];usedSize++;}}public void createHeap() {for (int parent = (usedSize-1-1)/2; parent >= 0 ; parent--) {shiftDown(parent,usedSize);}}private void shiftDown(int parent, int usedSize) {int child = (2*parent)+1;//左孩子下标while (child < usedSize) {if(child+1 < usedSize && elem[child] < elem[child+1]) {child++;}//child一定是 左右孩子最大值的下标if(elem[child] > elem[parent]) {swap(child, parent);parent = child;child = 2*parent+1;}else {//已经是大根堆了break;}}}private void swap(int i,int j) {int tmp = elem[i];elem[i] = elem[j];elem[j] = tmp;}public void offer(int val) {if(isFull()) {this.elem = Arrays.copyOf(elem,2*elem.length);}this.elem[usedSize] = val;//usedSize=10//向上调整shiftUp(usedSize);usedSize++;}private void shiftUp(int child) {int parent = (child-1)/2;while (child > 0) {if (elem[child] > elem[parent]) {swap(child,parent);child = parent;parent = (child-1)/2;} else {break;}}}public boolean isFull() {return usedSize == elem.length;}public int poll() {int tmp = elem[0];swap(0,usedSize-1);usedSize--;shiftDown(0,usedSize);return tmp;}public void heapSort() {int end = usedSize-1;while (end > 0) {swap(0,end);shiftDown(0,end);end--;}}
}
关于堆排序的问题

上面我们分析了“向上调整”和“向下调整”的时间复杂度,我们知道“向下调整”优于“向上调整”,下面将使用“向下调整”来实现堆排序。

实现堆排序的步骤:

1.首先建好堆

2.其次定义一个变量end标记最后一个元素的位置

3.然后交换堆顶和堆尾的元素

4.然后对堆顶位置进行向下调整,end--;

5.重复执行步骤3,4,5,直到end=0

堆排序代码
    public void heapSort() {int end = usedSize-1;while (end > 0) {swap(0,end);shiftDown(0,end);end--;}}private void shiftDown(int parent, int usedSize) {int child = (2*parent)+1;//左孩子下标while (child < usedSize) {if(child+1 < usedSize && elem[child] < elem[child+1]) {child++;}//child一定是 左右孩子最大值的下标if(elem[child] > elem[parent]) {swap(child, parent);parent = child;child = 2*parent+1;}else {//已经是大根堆了break;}}}private void swap(int i,int j) {int tmp = elem[i];elem[i] = elem[j];elem[j] = tmp;}
关于对象的比较
基于Comparable的比较

对于自定义类型,如果要对其进行比较,需要实现Comparable的compareTo方法。

代码示例

public class Person implements Comparable<Person> {  private String name;  private int age;  // 构造器、getter和setter省略  @Override  public int compareTo(Person o) {  return this.age - o.age; // 以年龄为基准进行比较  }  
}  // 使用示例  
Person p1 = new Person("Alice", 30);  
Person p2 = new Person("Bob", 25);  
System.out.println(p1.compareTo(p2)); // 输出正数,因为p1的年龄大于p2
基于Comparator的比较

对于自定义类型,如果要对其进行比较,需要实现Comparator的compare方法。

代码示例

import java.util.Comparator;  public class Person {  private String name;  private int age;  // 构造器、getter和setter省略  // 使用Comparator进行比较  public static Comparator<Person> byName = new Comparator<Person>() {  @Override  public int compare(Person p1, Person p2) {  return p1.getName().compareTo(p2.getName());  }  };  // 或者使用Lambda表达式(Java 8+)  public static Comparator<Person> byAge = (Person p1, Person p2) -> Integer.compare(p1.getAge(), p2.getAge());  
}  // 使用示例  
Person p1 = new Person("Alice", 30);  
Person p2 = new Person("Bob", 25);  
System.out.println(Person.byName.compare(p1, p2)); // 按名字比较  
System.out.println(Person.byAge.compare(p1, p2));  // 按年龄比较

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

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

相关文章

Django 搭建数据管理web——商品管理

教材&#xff1a; python web 企业级项目开发教程 黑马程序员 5.4 实例1&#xff1a;商品管理 实验步骤&#xff1a; 1.创建项目&#xff08;任意名字&#xff09;和应用&#xff08;goods&#xff09; 2.在项目文件夹&#xff08;manage.py文件所在路径&#xff09;新建te…

C语言中操作符详解(中)

C语言中操作符详解中 放在最前面的1、操作数&#xff08;Operands&#xff09;2、单目操作符2.1、分类2.2、举例分析&#xff08;上代码&#xff09; 3、关系操作符3.1、分类3.2、举例分析&#xff08;上代码&#xff09; 4、逻辑操作符4.1、分类4.2、举例分析&#xff08;上代…

生成模型——扩散模型(Diffusion Model)

一、扩散模型简介 扩散模型&#xff08;Diffusion Model&#xff09;是一种生成模型&#xff0c;主要用于图像生成等任务。它的基本原理源于扩散过程的物理概念&#xff0c;通过最小化去噪过程中的重建损失&#xff08;通常使用均方误差&#xff09;来训练模型&#x…

ssm101珠宝首饰交易平台开发+jsp.zip(论文+源码)_kaic

毕业设计&#xff08;论文&#xff09; 珠宝首饰交易平台 学 院 专 业 班 级 学 号 用户姓名 指导教师 完成日期 …

关于我、重生到500年前凭借C语言改变世界科技vlog.18——内存函数

文章目录 1. memcpy函数2. memmove函数3. memset函数4. memcmp函数希望读者们多多三连支持小编会继续更新你们的鼓励就是我前进的动力&#xff01; 内存函数是用于 操作内存块的一组函数&#xff0c;它们可以对内存进行复制、移动、设置和比较等操作。这些函数主要在 <str…

Redis高可用-主从复制

这里写目录标题 Redis主从复制主从复制过程环境搭建从节点配置常见问题主从模式缺点 Redis主从复制 虽然 Redis 可以实现单机的数据持久化&#xff0c;但无论是 RDB 也好或者 AOF 也好&#xff0c;都解决不了单点宕机问题&#xff0c;即一旦 redis 服务器本身出现系统故障、硬…

NVR设备ONVIF接入平台EasyCVR视频融合平台社会面视频资源接入视频专网,应该如何处理?

在数字化时代&#xff0c;视频监控系统已成为社会安全管理的重要组成部分。随着城市化进程的加速和信息技术的发展&#xff0c;如何有效整合和管理跨区域、跨行业的视频监控资源&#xff0c;成为了提升社会治理能力的关键。 EasyCVR视频融合云平台&#xff0c;作为TSINGSEE青犀…

通过全球最前沿的技术解决视频拼接中时延带来的的应用缺陷,使得全景视频拼接能够真正得以大范围使用和推广的智慧地产开源了。

智慧地产视觉监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;从而大大减少企业级应用约95%的开发成本。通过计算机视觉和…

推荐一款好用的postman替代工具2024

Apifox 是国内团队自主研发的 API 文档、API 调试、API Mock、API 自动化测试一体化协作平台&#xff0c;是非常好的一款 postman 替代工具。 它通过一套系统、一份数据&#xff0c;解决多个系统之间的数据同步问题。只要定义好接口文档&#xff0c;接口调试、数据 Mock、接口…

java作业项目以及azkaban的操作

参考内容&#xff1a; azkaban简介及azkaban部署、原理和使用介绍 1.在azkaban创建project 2.上传flow文件和project文件的压缩包 flow文件内容: nodes:- name: Testtype: commandconfig:command: java -jar /data/job/mtm-job-0.0.1-SNAPSHOT.jar --spring.profiles.activ…

【重生之我要苦学C语言】深入理解指针5

深入理解指针5 回调函数 回调函数就是一个通过函数指针调用的函数 如果你把函数的指针(地址)作为参数传递给另一个函数&#xff0c;当这个指针被用来调用其所指向的函数时&#xff0c;被调用的函数就是回调函数 回调函数不是由该函数的实现方直接调用&#xff0c;而是在特定的…

IOPaint模型部署教程

一、介绍 IOPaint是一款功能全面且强大的AI图像处理工具&#xff0c;它不仅免费开源&#xff0c;还由SOTA&#xff08;State-of-the-Art&#xff0c;即最先进&#xff09;AI模型驱动&#xff0c;为图像编辑和修复带来了前所未有的便利和高效。以下是对IOPaint的详细介绍&#…

吊打面试官系列:hashCode() 相同,equals() 就一定相等吗?

在编程的世界里&#xff0c;hashCode() 和 equals() 是一对形影不离的好兄弟。它们在Java中定义于Object类中&#xff0c;是每个Java对象都继承的两个方法。但是&#xff0c;如果你认为只要两个对象的hashCode()相同&#xff0c;它们的equals()就一定相等&#xff0c;那你就大错…

阿托伐他汀降脂疗效与安全性真实世界数据整理!

2024年9月&#xff0c;《中国医疗保险》杂志发布了题为《阿托伐他汀仿制药治疗高脂血症疗效与安全性的多中心回顾性队列研究》的重要研究结果。该研究由首都医科大学宣武医院牵头&#xff0c;联合上海交通大学医学院附属瑞金医院、吉林大学第一医院等10家国内顶尖三甲医院共同完…

深入剖析【C++继承】:单一继承与多重继承的策略与实践,解锁代码复用和多态的编程精髓,迈向高级C++编程之旅

​​​​​​​ &#x1f31f;个人主页&#xff1a;落叶 &#x1f31f;当前专栏: C专栏 目录 继承的概念及定义 继承的概念 继承定义 定义格式 继承基类成员访问⽅式的变化 继承类模板 基类和派⽣类间的转换 继承中的作⽤域 隐藏规则 成员函数的隐藏 考察继承【作⽤…

“嵌入”在大语言模型中是解决把句子转换成向量表示的技术

上一篇&#xff1a;《人工智能是这样理解“情绪”的》 序言&#xff1a;这段话要优化吗&#xff1f;““嵌入”是一种将句子、单词或其他语言单位转换为向量表示的技术。这个向量通常位于高维空间中&#xff0c;它以一种能够表达相似性的方式编码出文本的含义或上下文。嵌入层…

操作系统——内存分区管理

本章主要讨论为什么要给内存进行划分和如何划分的问题。 为了给每一个进程都分配一个大小合适的内存块 以连续存储进程的程序和数据&#xff0c;使得各进程可以并发执行 目录 一、内存的划分方法 1、固定分区法 2、动态分区法 3、动态分区的数据管理结构 二、分区的分配与回…

ML 系列: 第 24 节 — 离散概率分布(泊松分布)

目录 一、说明 二、固定时间间隔示例 三、固定间隔的示例 四、泊松分布的主要特征 五、示例 5.1 平均客户数的计算&#xff1a; 5.2 用于计算和绘制泊松分布的 Python 代码&#xff1a; 一、说明 泊松概率分布是一种离散概率分布&#xff0c;它表示在固定的时间或空间间隔内发生…

【comfyui教程】如何用 ComfyUI 修复和上色老照片?详细教程让老照片焕发新生

前言 如何用 ComfyUI 修复和上色老照片&#xff1f;详细教程让老照片焕发新生 老照片承载着无数回忆&#xff0c;可时光不饶人&#xff0c;随着岁月流逝&#xff0c;它们渐渐变得模糊、泛黄&#xff0c;甚至出现了褪色、裂痕。对于想要留住这份珍贵记忆的人来说&#xff0c;修…

ThinkServer SR658H V2服务器BMC做raid与装系统

目录 前提准备 一. 给磁盘做raid 二. 安装系统 前提准备 磁盘和系统BMC地址都已经准备好&#xff0c;可正常使用。 例&#xff1a; 设备BMC地址&#xff1a;10.99.240.196 一. 给磁盘做raid 要求&#xff1a; 1. 将两个894G的磁盘做成raid1 2. 将两块14902G的磁盘各自做…