模拟实现优先级队列

目录

定义

特点

构造函数

常用方法

关于扩容的问题

关于建堆的问题

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

(向上调整)代码 

(向下调整)代码 

关于入队列和出队列问题

模拟实现优先级队列代码

关于堆排序的问题

堆排序代码

关于对象的比较

基于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.mzph.cn/web/59339.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Autosar CP DDS规范导读

Autosar CP DDS 主要用途 数据通信 中间件协议&#xff1a;作为一种中间件协议&#xff0c;DDS实现了应用程序之间的高效数据通信&#xff0c;能够在不同的软件组件和ECU之间传输数据&#xff0c;确保数据的实时性和可靠性。跨平台通信&#xff1a;支持在AUTOSAR CP平台上的不同…

数字IC实践项目(10)—基于System Verilog的DDR4 Model/Tb 及基础Verification IP的设计与验证(付费项目)

数字IC实践项目&#xff08;10&#xff09;—基于System Verilog的DDR4 Model/Tb 及基础Verification IP的设计与验证&#xff08;付费项目&#xff09; 前言项目框图1&#xff09;DDR4 Verification IP2&#xff09;DDR4 JEDEC Model & Tb 项目文件1&#xff09;DDR4 Veri…

Jmeter中的监听器(三)

9--断言结果 功能特点 显示断言结果&#xff1a;列出所有断言的结果&#xff0c;包括通过和失败的断言。详细信息&#xff1a;显示每个断言的详细信息&#xff0c;如断言类型、实际结果和期望结果。错误信息&#xff1a;显示断言失败时的错误信息&#xff0c;帮助调试。颜色编…

人工智能大比拼(3)

已知x-,y-6&#xff0c;且下述表达式的值与x的取值无关&#xff0c;求y -10x2y7xy 上述这个很简单的数学题&#xff0c;可是在各家AI之间出现了争议&#xff0c;本期我使用了四个AI&#xff1a;kimi&#xff0c;商量&#xff0c;文心一言&#xff0c;chatyy 先来看一下kimi的表…

Xilinx Aurora 8B/10B IP介绍以及下板验证

文章目录 一、什么是Aurora协议&#xff1f;二、Aurora 8B/10B IP核的结构原理三、Aurora 8B/10B IP核 延迟开销四、用户数据接口格式4.1 AXI4-Stream 位排序4.2 帧传输用户端口说明4.3 帧传输数据流程4.4 Aurora 8B/10B 帧格式4.5 帧格式数据传输时序4.5.1 简单数据传输4.5.2 …

Redis8:商户查询缓存2

欢迎来到“雪碧聊技术”CSDN博客&#xff01; 在这里&#xff0c;您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者&#xff0c;还是具有一定经验的开发者&#xff0c;相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导&#xff0c;我将…

Camera Tuning中AE/AWB/AF基础知识介绍

3A定义 3A是Camera ISP控制算法的一个重要组成部分&#xff0c;通常分为自动曝光&#xff08;AE&#xff09;、自动聚焦&#xff08;AF&#xff09;、自动白平衡&#xff08;AWB&#xff09;三个组件。 自动曝光&#xff08;Auto Exposure&#xff09; AE基本概念 曝光概念…

数据库中的用户管理和权限管理

​ 我们进行数据库操作的地方其实是数据库的客户端&#xff0c;是我们在客户端将操作发送给数据库的服务器&#xff08;MySQL的服务器是mysqld&#xff09;&#xff0c;由数据库处理之后发送回来处理结果&#xff08;其实就是一种网络服务&#xff09;。所以可以存在多个客户端…

ubuntu24.04安装matlab失败

又是摸鱼摆烂的一天&#xff0c;好难过&#xff5e; 官方教程&#xff1a;https://ww2.mathworks.cn/help/install/ug/install-products-with-internet-connection.html 问题描述&#xff1a;https://ww2.mathworks.cn/matlabcentral/answers/2158925-cannot-install-matlab-r2…

Hive1.2.1与Hbase1.4.13集成---版本不兼容问题

hive与hbase集成失败,汗流夹背了吧老弟......哈哈哈哈,刷到这篇文章,那你可真是太幸运啦~ 常见错误一: FAILED: Execution Error, return code 1 from org.apache.hadoop.hive.ql.exec.DDLTask. org.apache.hadoop.hbase.HTableDescriptor.addFamily(Lorg/apache/hadoop/h…

项目1:井字棋 --- 《跟着小王学Python》

项目1&#xff1a;井字棋 — 《跟着小王学Python新手》 文章目录 项目1&#xff1a;井字棋 --- 《跟着小王学Python新手》目标功能设计1. 数据结构2. 功能模块3. 用户界面 实现步骤代码实现测试注意事项小结 目标 本技术文章旨在指导读者如何使用 Python 编程语言来实现一个简…

Python 的 Pygame 库,编写简单的 Flappy Bird 游戏

Pygame 是一个用 Python 编写的开源游戏开发框架&#xff0c;专门用于编写 2D 游戏。它提供了丰富的工具和功能&#xff0c;使得开发者能够快速实现游戏中的图形渲染、声音播放、输入处理和动画效果等功能。Pygame 非常适合初学者和想要快速创建游戏原型的开发者。 Pygame 的主…

LeetCode-222.完全二叉树的节点个数

. - 力扣&#xff08;LeetCode&#xff09; 给你一棵 完全二叉树 的根节点 root &#xff0c;求出该树的节点个数。 完全二叉树 的定义如下&#xff1a;在完全二叉树中&#xff0c;除了最底层节点可能没填满外&#xff0c;其余每层节点数都达到最大值&#xff0c;并且最下面一…

【MongoDB】MongoDB的核心-索引原理及索引优化、及查询聚合优化实战案例(超详细)

文章目录 一、数据库查询效率问题引出索引需求二、索引的基本原理及作用&#xff08;一&#xff09;索引的创建及数据组织&#xff08;二&#xff09;不同类型的索引&#xff08;三&#xff09;索引的额外属性 三、索引的优化与查询计划分析&#xff08;一&#xff09;通过prof…

企业如何实现无缝数据中心进行大数据迁移呢?

数据中心迁移是企业面临的一个复杂而关键的挑战&#xff0c;涉及到大量的数据移动和系统的重新部署。随着业务的扩展和技术的进步&#xff0c;企业可能需要将数据中心迁移到新的位置或升级到更先进的设备。在进行数据迁移时&#xff0c;必须精心规划和执行&#xff0c;以确保数…

初始JavaEE篇 —— 网络编程(2):了解套接字,从0到1实现回显服务器

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a;JavaEE 目录 TCP 与 UDP Socket套接字 UDP TCP 网络基础知识 在一篇文章中&#xff0c;我们了解了基础的网络知识&#xff0c;网络的出…

【人工智能】10分钟解读-深入浅出大语言模型(LLM)——从ChatGPT到未来AI的演进

文章目录 一、前言二、GPT模型的发展历程2.1 自然语言处理的局限2.2 机器学习的崛起2.3 深度学习的兴起2.3.1 神经网络的训练2.3.2 神经网络面临的挑战 2.4 Transformer的革命性突破2.4.1 Transformer的核心组成2.4.2 Transformer的优势 2.5 GPT模型的诞生与发展2.5.1 GPT的核心…

最全最简单理解迭代器

1. 迭代器的基础概念(iterator) 1.1 本质 迭代器能够用来遍历容器的对象,与能够遍历数组的指针类似,是广义指针。 1.2 作用: 能够让迭代器与算法不干扰的相互发展,最后又能无间隙的粘合起来。重载了*,++,==,!=,=运算符。用以操作复杂的数据结构。容器提供迭代…

MTSET可溶于DMSO、DMF、THF等有机溶剂,并在水中有轻微的溶解性,91774-25-3

一、基本信息 中文名称&#xff1a;[2-(三甲基铵)乙基]甲硫基磺酸溴&#xff1b;MTSET巯基反应染料 英文名称&#xff1a;MTSET&#xff1b;[2-(Trimethylammonium)ethyl]methanethiosulfonate Bromide CAS号&#xff1a;91774-25-3 分子式&#xff1a;C6H16BrNO2S2 分子量…

CC1链学习记录

&#x1f338; 前言 上篇文章学习记录了URLDNS链&#xff0c;接下来学习一下Common-Colections利用链。 &#x1f338; 相关介绍 Common-Colections是Apache软件基金会的项目&#xff0c;对Java标准的Collections API提供了很好的补充&#xff0c;在其基础上对常用的数据结构…