PriorityQueue 阅读记录

1、前言

1、优先队列,底层通过数组来构造树(二叉树) 来实现的。

2、默认是最小堆(取出来的是最小值),可以通过传入一个比较器 comparator 来构造一个最大堆。

3、传入的参数不能为空,否则抛出NPE问题。

4、最大堆的特性是左右孩子的值都比父节点小, 最小堆是左右节点值都比父节点大,利用该特性可以用来解决一些topK问题

2、关键常量
// 初始化容量
private static final int DEFAULT_INITIAL_CAPACITY = 11;// 最小堆和最大堆的比较器, 默认是最小堆
private final Comparator<? super E> comparator;// 数据存储的数组,构造成树后,节点在数组的index 可以通过以下公式求出
// leftNo = parentNo*2+1  左节点的index
// rightNo = parentNo*2+2 右节点的index
// parentNo = (nodeNo-1)/2 //父节点的index , nodeNo当前节点的index
transient Object[] queue;// 最大数组可分配的大小, 这里 - 8 是因为一些虚拟机在数组中会保留头部字段,
// 如果不 -8,分配大小的时候有可能导致OutOfMemoryError
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
3、源码解析

下边记录下对一些方法的理解。

3.1、扩容
   /*** 扩容** @param minCapacity 期望的最小容量*/private void grow(int minCapacity) {int oldCapacity = queue.length;// 扩容两倍,如果还是小,那么而外加 50%int newCapacity = oldCapacity + ((oldCapacity < 64) ?(oldCapacity + 2) :(oldCapacity >> 1));// 防止新容量大于数组的最大容量if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);queue = Arrays.copyOf(queue, newCapacity);}private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // 最小容量 < 0 ,抛出异常throw new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;}
3.2、add 方法
public boolean add(E e) {return offer(e);}/*** 入队,不能插入 null 元素*/public boolean offer(E e) {// 传入null元素,抛异常if (e == null)throw new NullPointerException();modCount++;int i = size;// 如果当前容量大于数组长度,扩容if (i >= queue.length)grow(i + 1);size = i + 1;// 如果当前容量为空,表示为新队列,直接放在首位即可if (i == 0)queue[0] = e;// 否则直接插入,调整,保证堆的合理性elsesiftUp(i, e);return true;}/*** 插入值后,根据比较器进行结构调整,维持堆的特性,向上调整*/private void siftUp(int k, E x) {if (comparator != null)siftUpUsingComparator(k, x);elsesiftUpComparable(k, x);}/*** 最小堆的结构调整,从k指定的位置开始,* 将x逐层与当前点的parent进行比较并交换,直到满足x >= queue[parent]为止。* 注意这里的比较可以是元素的自然顺序,也可以是依靠比较器的顺序。*/private void siftUpComparable(int k, E x) {Comparable<? super E> key = (Comparable<? super E>) x;// k 指定的位置indexwhile (k > 0) {// 根据公式求出 parentNo = (nodeNo-1)/2 父节点的 indexint parent = (k - 1) >>> 1;Object e = queue[parent];// 从k当前位置的parent进行值比较,如果x 大于 parent 就停止循环。(最小堆子节点不能小于父节点)if (key.compareTo((E) e) >= 0)break;// 交换值queue[k] = e;k = parent;}queue[k] = key;}/** @link siftUpComparable() 方法差不多,根据传入比较器值的大小来进行堆的结构调整* 可能是最大堆,也可能是最小堆*/private void siftUpUsingComparator(int k, E x) {while (k > 0) {int parent = (k - 1) >>> 1;Object e = queue[parent];// 用比较器的比较方法进行比较if (comparator.compare(x, (E) e) >= 0)break;queue[k] = e;k = parent;}queue[k] = x;}

关于堆siftUp的结构调整,如下图所示:(图来自https://github.com/CarpenterLee/JCFInternals/blob/master/markdown/8-PriorityQueue.md)

 3.3、peek()
    // 获取堆顶元素public E peek() {return (size == 0) ? null : (E) queue[0];}
3.4、poll()
 /** 弹出堆的第一个元素,同时将最后一个元素置 null , 然后进行堆的结构调整,向下调整。*/public E poll() {if (size == 0)return null;int s = --size;modCount++;// 获取堆第一个元素E result = (E) queue[0];// 获取堆最后一个元素,将其置nullE x = (E) queue[s];queue[s] = null;// 堆的结构调整if (s != 0)siftDown(0, x);return result;}/*** 插入 x 的值放在 k 的位置上,为了保证堆的特性,根据比较器来进行调整堆的结构。* * @param k 要插入的位置* @param x 要插入的值*/private void siftDown(int k, E x) {if (comparator != null)siftDownUsingComparator(k, x);elsesiftDownComparable(k, x);}/** 堆结构向下调整,从k指定的位置开始,* 将x逐层向下与当前点的左右孩子中较小的那个交换,直到x小于或等于左右孩子中的任何一个为止。*/private void siftDownComparable(int k, E x) {Comparable<? super E> key = (Comparable<? super E>)x;// 获取数组的中点位置int half = size >>> 1;        // loop while a non-leafwhile (k < half) {// 获取k左孩子的index(假设左孩子是最小的)int child = (k << 1) + 1; Object c = queue[child];// 获取k右孩子的index, 为左孩子index + 1int right = child + 1;// 将 x 逐层向下与当前点的左右孩子中较小的那个交换,// 直到 x 小于或等于左右孩子中的任何一个为止if (right < size &&((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)// 左右孩子最小节点的值c = queue[child = right];// 如果父节点比孩子节点还小,则结束循环if (key.compareTo((E) c) <= 0)break;// 交互当前父节点和比较小节点的值queue[k] = c;// 记录孩子节点的index, 继续循环k = child;}// 最终把k节点,也就是父节点放在合适的位置上,维持堆的特性queue[k] = key;}/** 根据用户构造的比较器,向下调整堆结构,从k指定的位置开始,* 将x逐层向下与当前点的左右孩子中较小的那个交换,直到x小于或等于左右孩子中的任何一个为止。*/private void siftDownUsingComparator(int k, E x) {// 获取数组的中点位置int half = size >>> 1;while (k < half) {int child = (k << 1) + 1;Object c = queue[child];int right = child + 1;if (right < size &&// 使用用户自定义的比较器进行比较comparator.compare((E) c, (E) queue[right]) > 0)c = queue[child = right];if (comparator.compare(x, (E) c) <= 0)break;// 交换值queue[k] = c;k = child;}queue[k] = x;}

 关于堆的向下调整大概图,如下所示:(图来着https://github.com/CarpenterLee/JCFInternals/blob/master/markdown/8-PriorityQueue.md)

3.5、remove()
// 删除元素,如果元素不存在,返回falsepublic boolean remove(Object o) {int i = indexOf(o);if (i == -1)return false;else {removeAt(i);return true;}}// 遍历查找元素是否存在于队列中,存在则返回其索引 ,否则返回-1private int indexOf(Object o) {if (o != null) {for (int i = 0; i < size; i++)if (o.equals(queue[i]))return i;}return -1;}// 根据索引删除元素private E removeAt(int i) {// 假设索引大于等于0, 同时索引小于数组大小modCount++;int s = --size;// 如果元素刚好是最后一个,直接置 null 即可if (s == i)queue[i] = null;else {// 找到最后的一个元素,保存最后一个元素的值,然后删除该值E moved = (E) queue[s];queue[s] = null;// 这时候需要向下调整堆的结构siftDown(i, moved);// 如果当前索引i的值还等于调整前最后一个元素的值if (queue[i] == moved) {// 向上调整siftUp(i, moved);// 调整完之后,如果当前索引i的值不等于调整前最后一个元素的值if (queue[i] != moved)// 返回删除的元素的值return moved;}}// 没找到,返回nullreturn null;}

remove(i)堆调整的大概图如下所示:(图来自https://github.com/CarpenterLee/JCFInternals/blob/master/markdown/8-PriorityQueue.md)

手动结束线~~~~~~~

对于其它方法,感兴趣的朋友可以自行去研究下。

4、参考

JCFInternals/markdown/8-PriorityQueue.md at master · CarpenterLee/JCFInternals · GitHub大佬的集合源码分析,写的很nice,图都来自这上面的。

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

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

相关文章

Study--Oracle-07-ASM自动存储管理(一)

一、ASM实例和数据库实例对应关系 1、ASM是Oracle 10g R2中为了简化Oracle数据库的管理而推出来的一项新功能&#xff0c;这是Oracle自己提供的卷管理器&#xff0c;主要用于替代操作系统所提供的LVM&#xff0c;它不仅支持单实例&#xff0c;同时对RAC的支持也是非常好。ASM可…

汽车开发阶段(OTS/VFF/PVS/OS/SOP)

OTS&#xff1a;即英语中的Off Tooling Sample&#xff0c;通常被称为工装样件。它指的是通过配套设备、工装夹具以及模具制造出来的样品&#xff0c;但并不强调生产的时间效率&#xff0c;主要用于验证产品的设计能力。 VFF&#xff1a;在德语中表示为Vorserien Freigabefahr…

集成excel工具:自定义导入回调监听器、自定义类型转换器、web中的读、捕获文件格式转换错误ExcelDataConvertException

文章目录 I 封装导入导出1.1 定义工具类1.2 自定义读回调监听器: 回调业务层处理导入数据1.3 定义文件导入上下文1.4 定义回调协议II 自定义转换器2.1 自定义枚举转换器2.2 日期转换器2.3 时间、日期、月份之间的互转2.4 LongConverterIII web中的读3.1 使用默认回调监听器3.2…

C++基础知识:C++内存分区模型,全局变量和静态变量以及常量,常量区,字符串常量和其他常量,栈区,堆区,代码区和全局区

1.C内存分区模型 C程序在执行时&#xff0c;将内存大方向划分为4个区域 代码区:存放函数体的二进制代码&#xff0c;由操作系统进行管理的&#xff08;在编译器中所书写的代码都会存放在这个空间。&#xff09; 全局区:存放全局变量和静态变量以及常量 栈区:由编译器自动分…

Mysql具体数据操作和表的约束(上)

表中数据的增删改查 插入数据(添加数据) 1.按指定字段插入数据:insert into <表名> (字段1,字段2,...) values (),(),.... 注意1:values后面的括号是指行数(几条记录),一个括号表示插入一条记录,多个括号以此类推 注意2:values后面括号内部插入的数据…

【python学习】第三方库之pandas库的定义、特点、功能、使用场景和代码示例

引言 pandas是一个强大的Python库&#xff0c;用于数据分析和数据处理。它基于NumPy&#xff0c;提供了灵活的数据结构&#xff08;Series和DataFrame&#xff09;和数据操作功能&#xff0c;是数据科学和机器学习中不可或缺的工具 文章目录 引言一、安装pandas第三方库二、pan…

nginx反向代理实例

一. 准备工作 1.1 ngnix的安装 nginx基本概念和安装-CSDN博客 1.2 安装tomcat tomcat服务器是一个免费的开放源代码的Web应用服务器&#xff0c;属于轻量级应用服务器&#xff0c;适用于中小型系统和并发访问用户不是很多的情况。 前往官网网站&#xff1a;Apache Tomcat - Ap…

C++迈向精通:模板中的引用与remove_reference原理

remove_reference 原理 模板中的引用参数 在模板中&#xff0c;双 &‘ 会被解析为“引用”&#xff0c;这个“引用”可以是“左值”引用&#xff0c;也可以是“右值”引用。 例如&#xff1a; template <typename T> void func(T &&a) {std::cout <&l…

从零开始接触人工智能大模型,该如何学习?

人工智能是计算机科学领域中最具前瞻性和影响力的技术之一。它是一种智慧型算法&#xff0c;能够模拟人类的思维过程&#xff0c;处理大量的数据和信息&#xff0c;从而发现隐藏在其中的规律和趋势。人工智能的应用范围非常广泛&#xff0c;包括语音识别、图像识别、自然语言处…

《简历宝典》14 - 简历中“项目经历”,实战讲解,前端篇

上一节我们针对项目经历做了内功式的讲解&#xff0c;为了加深读者的印象&#xff0c;可以更轻松的套用到自己的简历上&#xff0c;本章继续从前端开发、Java开发以及软件测试的三个角度&#xff0c;再以校招和初级、中级以及高级三个维度分别入手&#xff0c;以实战讲解的形式…

gihub导入gitee仓库实现仓库同步

昨天在GitHub里导入了gitee仓库&#xff0c;但是在仓库同步这里卡了很久&#xff0c;因为网上大多数都是从github导入gitee&#xff0c;然后github生成token放入实现同步&#xff0c;但是我找到一种更为方便的&#xff01; 1.首先找到项目文件下的.git文件里的config文件 2.在…

Python实战MySQL之数据库操作全流程详解

概要 MySQL是一种广泛使用的关系型数据库管理系统,Python可以通过多种方式与MySQL进行交互。本文将详细介绍如何使用Python操作MySQL数据库,包括安装必要的库、连接数据库、执行基本的CRUD(创建、读取、更新、删除)操作,并包含具体的示例代码,帮助全面掌握这一过程。 准…

dom4j 操作 xml 之按照顺序插入标签

最近学了一下 dom4j 操作 xml 文件&#xff0c;特此记录一下。 public class Dom4jNullTagFiller {public static void main(String[] args) throws DocumentException {SAXReader reader new SAXReader();//加载 xml 文件Document document reader.read("C:\\Users\\24…

基于jeecgboot-vue3的Flowable流程支持bpmn流程设计器与仿钉钉流程设计器-编辑多版本处理

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 1、前端编辑带有仿钉钉流程的处理 /** 编辑流程设计弹窗页面 */const handleLoadXml (row) > {console.log("handleLoadXml row",row)const params {flowKey: row.key,ver…

搜集日志。

logstash 负责&#xff1a; 接收数据 input — 解析过滤并转换数据 filter(此插件可选) — 输出数据 output input — decode — filter — encode — output elasticsearch 查询和保存数据 Elasticsearch 去中心化集群 Data node 消耗大量 CPU、内存和 I/O 资源 分担一部分…

四、GD32 MCU 常见外设介绍

系统架构 1.RCU 时钟介绍 众所周知&#xff0c;时钟是MCU能正常运行的基本条件&#xff0c;就好比心跳或脉搏&#xff0c;为所有的工作单元提供时间 基数。时钟控制单元提供了一系列频率的时钟功能&#xff0c;包括多个内部RC振荡器时钟(IRC)、一个外部 高速晶体振荡器时钟(H…

Docker修改Postgresql密码

在Docker环境中&#xff0c;对已运行的PostgreSQL数据库实例进行密码更改是一项常见的维护操作。下面将详述如何通过一系列命令行操作来实现这一目标。 修改方式 查看容器状态及信息 我们需要定位到正在运行的PostgreSQL容器以获取其相关信息。执行以下命令列出所有正在运行…

Mongodb多键索引中索引边界的混合

学习mongodb&#xff0c;体会mongodb的每一个使用细节&#xff0c;欢迎阅读威赞的文章。这是威赞发布的第93篇mongodb技术文章&#xff0c;欢迎浏览本专栏威赞发布的其他文章。如果您认为我的文章对您有帮助或者解决您的问题&#xff0c;欢迎在文章下面点个赞&#xff0c;或者关…

安全防御---防火墙双击热备与带宽管理

目录 一、实验拓扑 二、实验需求 三、实验的大致思路 四、实验过程 4、基础配置 4.1 FW4的接口信息 4.2 新建办公&#xff0c;生产&#xff0c;游客&#xff0c;电信&#xff0c;移动安全区域 4.3 接口的网络配置 生产区:10.0.1.2/24 办公区:10.0.2.2/24 4.4 FW4的…

极地生产力自主采样系统的观测:融池比例统计 MEDEA 融池比例数据集

Observations from the Autonomous Polar Productivity Sampling System. 极地生产力自主采样系统的观测结果 简介 该项目是美国国家航空航天局 ICESCAPE 大型项目的一部分&#xff0c;旨在研究浮游植物丰度的长期季节性变化与整个生长季节在波弗特海和楚科奇海测量到的海冰…