【数据结构】优先级队列

news2024/9/24 9:25:35

⭐ 作者:小胡_不糊涂
🌱 作者主页:小胡_不糊涂的个人主页
📀 收录专栏:浅谈数据结构
💖 持续更文,关注博主少走弯路,谢谢大家支持 💖

PriorityQueue

  • 1. 什么是优先级队列
  • 2. 模拟实现
    • 2.1 堆
    • 2.2 堆的存储方式
    • 2.3 堆的创建
      • 2.3.1 向下调整
      • 2.3.2 堆的创建
      • 2.3.3 建堆的时间复杂度
    • 2.4 堆的插入与删除
      • 2.4.1 插入
      • 2.4.2 删除
  • 3. 常用接口介绍
    • 3.1 PriorityQueue的特性
    • 3.2 常用接口

1. 什么是优先级队列

首先,我们知道队列是一种先进先出(FIFO)的数据结构,但有些情况下,操作的数据可能带有优先级,一般出队列时,可能需要优先级高的元素先出队列。
该中场景下,使用队列显然不合适,比如:在手机上玩游戏的时候,如果有来电,那么系统应该优先处理打进来的电话。

在这种情况下,数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象。这种数据结构就是优先级队列(Priority Queue)

2. 模拟实现

JDK1.8中的PriorityQueue底层使用了堆这种数据结构,而堆实际就是在完全二叉树的基础上进行了一些调整。

2.1 堆

如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储 在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为小堆(或大堆)。

将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

堆的性质:

  • 堆中某个节点的值总是不大于或不小于其父节点的值;
  • 堆总是一棵完全二叉树。
    在这里插入图片描述

2.2 堆的存储方式

从堆的概念可知,堆是一棵完全二叉树,因此可以层序的规则采用顺序的方式来高效存储。
在这里插入图片描述
对于非完全二叉树,则不适合使用顺序方式进行存储,因为为了能够还原二叉树,空间中必须要存储空节点,就会导致空间利用率比较低。

将元素存储到数组中后,可以根据二叉树的性质对树进行还原。假设i为节点在数组中的下标,则有:

  • 如果i为0,则i表示的节点为根节点,否则i节点的双亲节点为 (i - 1)/2–向下取整
  • 如果2 * i + 1 小于节点个数,则节点i的左孩子下标为2 * i + 1,否则没有左孩子
  • 如果2 * i + 2 小于节点个数,则节点i的右孩子下标为2 * i + 2,否则没有右孩子

2.3 堆的创建

2.3.1 向下调整

对于集合{ 27,15,19,18,28,34,65,49,25,37 }中的数据,如果将其创建成堆呢?
在这里插入图片描述
仔细观察上图后发现:根节点的左右子树已经完全满足堆的性质,因此只需将根节点向下调整好即可。

向下调整过程(以小堆为例):

  1. 让parent标记需要调整的节点,child标记parent的左孩子(注意:parent如果有孩子一定先是有左孩子)
  2. 如果parent的左孩子存在,即:child < size,进行以下操作,直到parent的左孩子不存在
  • parent右孩子如果存在,找到左右孩子中最小的孩子,让child进行标记
  • 将parent与较小的孩子child比较,如果:parent小于较小的孩子child,调整结束;否则:交换parent与较小的孩子child,交换完成之后,parent中大的元素向下移动,可能导致子树不满足对的性质,因此需要继续向下调整,即parent = child;child = parent*2+1;然后继续第2步。

在这里插入图片描述
代码实现:

private void shiftDown(int parent,int len) {
        int child = 2*parent+1;
        //至少有左孩子:
        while (child < len) {
            //左孩子和右孩子比较大小,如果右孩子的值小,则那么
            if(child+1 < len && elem[child] > elem[child+1]) {
                child = child+1;
            }
            //走完上述if语句,在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;
    }

在调整以parent为根的二叉树时,必须要满足parent的左子树和右子树已经是堆了才可以向下调整。

时间复杂度分析:最坏的情况即图示的情况,从根一路比较到叶子,比较的次数为完全二叉树的高度,即时间复杂度为O(log2n)。

2.3.2 堆的创建

对于普通的序列{ 1,5,3,8,7,6 },即根节点的左右子树不满足堆的特性,又该如何调整呢?
在这里插入图片描述

public static void createHeap(int[] array) {
	// 找倒数第一个非叶子节点,从该节点位置开始往前一直到根节点,遇到一个节点,应用向下调整
	int root = ((array.length-1-1)>>1);//int root = ((array.length-1-1)/2);
	for (; root >= 0; root--) {
		shiftDown(array, root);
	}
}

2.3.3 建堆的时间复杂度

因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来看的就是近似值,多几个节点不影响最终结果):
在这里插入图片描述
因此:建堆的时间复杂度为O(N)

2.4 堆的插入与删除

2.4.1 插入

堆的插入总共需要两个步骤:

  1. 先将元素放入到底层空间中(注意:空间不够时需要扩容)
  2. 将最后新插入的节点向上调整,直到满足堆的性质
    在这里插入图片描述
public void shiftUp(int child) {
        // 找到child的双亲
        int parent = (child - 1) / 2;
        while (child > 0) {
            // 如果双亲比孩子小,parent满足堆的性质,调整结束
            if (elem[parent] < elem[child]) {
                break;
            }
            else{
                // 将双亲与孩子节点进行交换
                int t = elem[parent];
                elem[parent] = elem[child];
                elem[child] = t;
                // 大的元素向下移动,可能到值子树不满足对的性质,因此需要继续向上调增
                child = parent;
                parent = (child - 1) / 2;
            }
        }
    }

2.4.2 删除

堆的删除一定删除的是堆顶元素。具体如下:

  1. 将堆顶元素对堆中最后一个元素交换
  2. 将堆中有效数据个数减少一个
  3. 对堆顶元素进行向下调整

在这里插入图片描述

3. 常用接口介绍

3.1 PriorityQueue的特性

Java集合框架中提供了PriorityQueue和PriorityBlockingQueue两种类型的优先级队列,PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全的。

在这里插入图片描述

关于PriorityQueue的使用要注意:

  1. 使用时必须导入PriorityQueue所在的包,即:import java.util.PriorityQueue;
  2. PriorityQueue中放置的元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出ClassCastException异常
  3. 不能插入null对象,否则会抛出NullPointerException
  4. 没有容量限制,可以插入任意多个元素,其内部可以自动扩容
  5. 插入和删除元素的时间复杂度为O(log2N)
  6. PriorityQueue底层使用了堆数据结构
  7. PriorityQueue默认情况下是小堆—即每次获取到的元素都是最小的元素

3.2 常用接口

  1. 优先级队列的构造
构造器功能介绍
PriorityQueue()创建一个空的优先级队列,默认容量是11
PriorityQueue(intinitialCapacity)创建一个初始容量为initialCapacity的优先级队列,注意:initialCapacity不能小于1,否则会抛IllegalArgumentException异常
PriorityQueue(Collection<?extends E> c)用一个集合来创建优先级队列

上述方法的实现:

static void TestPriorityQueue(){
	// 创建一个空的优先级队列,底层默认容量是11
	PriorityQueue<Integer> q1 = new PriorityQueue<>();
	// 创建一个空的优先级队列,底层的容量为initialCapacity
	PriorityQueue<Integer> q2 = new PriorityQueue<>(100);
	ArrayList<Integer> list = new ArrayList<>();
	list.add(4);
	list.add(3);
	list.add(2);
	list.add(1);
	// 用ArrayList对象来构造一个优先级队列的对象
	PriorityQueue<Integer> q3 = new PriorityQueue<>(list);
	System.out.println(q3.size());//4
	System.out.println(q3.peek());//1
}
  1. 插入/删除/获取优先级最高的元素
函数名功能介绍
boolean offer(E e)插入元素e,插入成功返回true,如果e对象为空,抛出NullPointerException异常,时
间复杂度:O(log2N) ,注意:空间不够时候会进行扩容
E peek()获取优先级最高的元素,如果优先级队列为空,返回null
E poll()移除优先级最高的元素并返回,如果优先级队列为空,返回null
int size()获取有效元素的个数
void clear()清空
boolean isEmpty()检测优先级队列是否为空,空返回true

上述功能的实现:

static void TestPriorityQueue2(){
	int[] arr = {4,1,9,2,8,0,7,3,6,5};
	// 一般在创建优先级队列对象时,如果知道元素个数,建议就直接将底层容量给好
	// 否则在插入时需要不多的扩容
	// 扩容机制:开辟更大的空间,拷贝元素,这样效率会比较低
	PriorityQueue<Integer> q = new PriorityQueue<>(arr.length);
	for (int e: arr) {
		q.offer(e);
	}
	System.out.println(q.size()); // 打印优先级队列中有效元素个数 10
	System.out.println(q.peek()); // 获取优先级最高的元素 0
	// 从优先级队列中删除两个元素,再次获取优先级最高的元素
	q.poll();
	q.poll();
	System.out.println(q.size()); // 打印优先级队列中有效元素个数 8
	System.out.println(q.peek()); // 获取优先级最高的元素 2
	q.offer(0);
	System.out.println(q.peek()); // 获取优先级最高的元素 0
	// 将优先级队列中的有效元素删除掉,检测其是否为空
	q.clear();
	if(q.isEmpty()){
		System.out.println("优先级队列已经为空!!!");
	}else{
		System.out.println("优先级队列不为空");
	}
}

PriorityQueue的扩容方式:

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
	int oldCapacity = queue.length;
	// Double size if small; else grow by 50%
	int newCapacity = oldCapacity + ((oldCapacity < 64) ?(oldCapacity + 2) :(oldCapacity >> 1));
	// overflow-conscious code
	if (newCapacity - MAX_ARRAY_SIZE > 0)
	newCapacity = hugeCapacity(minCapacity);
	queue = Arrays.copyOf(queue, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
	if (minCapacity < 0) // overflow
		throw new OutOfMemoryError();
	return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;
}

优先级队列的扩容说明:

  • 如果容量小于64时,是按照oldCapacity的2倍方式扩容的
  • 如果容量大于等于64,是按照oldCapacity的1.5倍方式扩容的
  • 如果容量超过MAX_ARRAY_SIZE,按照MAX_ARRAY_SIZE来进行扩容

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

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

相关文章

热搜榜:最热门的话题文本排行榜API接口

近年来&#xff0c;随着社交媒体的兴起&#xff0c;热门话题的数量和更新速度都在不断增加&#xff0c;因此热门话题排行榜的需求也越来越大。在这篇文章中&#xff0c;我们将探讨如何使用#热搜榜# API接口获取最热门的话题文本排行榜&#xff0c;并给出相关代码示例。 一、API…

案例分析背诵点

案例分析背诵点 信息系统架构的设计理论与实践 信息系统架构的定义 软件或计算机系统的信息系统架构是该系统的一个&#xff08;或多个&#xff09;结构&#xff0c;而结构由软件元素&#xff0c;元素的外部可见属性及它们之间的关系组成。 信息系统架构的分类 按照信息系…

SAP ABAP 报表输出成 excel 统计图形 (RFC : GFW_PRES_SHOW_MULT)

SAP 预设了一个类型组 GFW &#xff0c;做简单的excel图形输出 话不多说&#xff0c;直接上代码&#xff1a; *&---------------------------------------------------------------------* *& Report ZCYCLE057 *&----------------------------------------------…

Kafka核心组件详解

1.概述 对于Kafka的学习&#xff0c;在研究其系统模块时&#xff0c;有些核心组件是指的我们去了解。今天给大家来剖析一下Kafka的一些核心组件&#xff0c;让大家能够更好的理解Kafka的运作流程。 2.内容 Kafka系统设计的非常优秀&#xff0c;它的核心组件由生产者、消费者…

基于 nodejs+vue电子书阅读系统mysql

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

day06-Flex布局

Flex布局 目标&#xff1a;熟练使用 Flex 完成结构化布局 01-标准流 标准流也叫文档流&#xff0c;指的是标签在页面中默认的排布规则&#xff0c;例如&#xff1a;块元素独占一行&#xff0c;行内元素可以一行显示多个。 02-浮动 基本使用 作用&#xff1a;让块元素水平排…

PyCharm中文使用详解

PyCharm是一个Python IDE&#xff0c;可以帮助程序员节省时间&#xff0c;提高生产力。那么具体怎么用呢&#xff1f;本文介绍了PyCharm的安装、插件、外部工具、专业功能等&#xff0c;希望对大家有所帮助。 之前没有系统介绍过PyCharm。如何配置环境&#xff0c;如何DeBug&a…

二叉树的序列化和反序列化

把内存中的数变为硬盘里字符串的形式 &#xff0c;要求得出的字符串对应唯一的二叉树 序列化&#xff1a;二叉树 → 字符串 反序列化&#xff1a;字符串 → 二叉树 先序遍历序列化 null用#表示&#xff0c;下划线_表示一个value值的结束 package binarytree;public class S…

基于 nodejs+vue购物网站设计系统mysql

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

DevOps持续集成-Jenkins(3)

文章目录 DevOpsDevOps概述Jenkins实战3&#xff1a;实战1和实战2的加强版&#xff08;新增SonarQube和Harbor&#xff09;⭐环境准备⭐项目架构图对比Jenkins实战1和实战2&#xff0c;新增内容有哪些&#xff1f;SonarQube教程采用Docker安装SonarQube &#xff08;在Jenkins所…

【python笔记】小甲鱼

P3 查看内置函数 dir(__builtins__) P4 变量名命名规则&#xff1a; 1、变量名不能以数字打头&#xff1b; 2、变量名可以是中文 字符串可以是&#xff1a; 1、单引号&#xff1a;文本中存在双引号时使用单引号 2、双引号&#xff1a;文本中存在单引号时使用双引号 当…

PPT文档图片设计素材资源下载站模板源码/织梦内核(带用户中心+VIP充值系统+安装教程)

源码简介&#xff1a; PPT文档图片设计素材资源下载站模板源码&#xff0c;作为织梦内核素材资源下载站源码&#xff0c;它自带了用户中心和VIP充值系统&#xff0c;也有安装教程。 织梦最新内核开发的模板&#xff0c;该模板属于素材下载、文档下载、图库下载、PPT下载、办公…

电商时代,VR全景如何解决实体店难做没流量?

近日&#xff0c;电商和实体经济的对立成为了热门话题&#xff0c;尽管电商的兴起确实对线下实体店造成了一定的冲击&#xff0c;但实体店也不是没有办法挽救。VR全景助力线下实体店打造线上店铺&#xff0c;打通流量全域布局&#xff0c;还能实现打开产品、查看产品内部细节等…

NewStarCTF2023week4-R通大残(RGB通道隐写)

最开始试了很多Misc常见的其他方向&#xff0c;啥也没找到... 后面重新仔细看了一下题目&#xff0c;联想到R通道&#xff0c;R是储存红色的通道&#xff0c;通道里有R&#xff08;红&#xff09;、G&#xff08;绿&#xff09;、B&#xff08;蓝&#xff09;三个通道&#xf…

Armv8/Armv9的VIPT的别名问题是如何解决的

https://www.cse.unsw.edu.au/~cs9242/02/lectures/03-cache/node8.html https://developer.arm.com/documentation/ddi0406/b/System-Level-Architecture/Virtual-Memory-System-Architecture–VMSA-/Address-mapping-restrictions

Day10力扣打卡

打卡记录 求一个整数的惩罚数&#xff08;预处理递归&#xff09; 链接 int PRE_SUM[1001];int init []() {for (int i 1; i < 1000; i) {string s to_string(i * i);int n s.length();function<bool(int, int)> dfs [&](int p, int sum) -> bool {if (…

【Java集合类面试二十二】、Map和Set有什么区别?

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;Map和Set有什么区别&…

Spring Cloud之负载均衡与服务调用(Ribbon)

目录 Ribbon 简介 负载均衡 简介 负载均衡方式 服务端负载均衡 工作原理 特点 客户端负载均衡 工作原理 特点 对比 实现 负载均衡策略 切换负载均衡策略 定制负载均衡策略 超时与重试 单个服务配置 全局配置 服务调用 示例 Ribbon 简介 Ribbon 是 Netfli…

【滴滴出行安全应急响应平台DSRC2倍积分卡】

1、使用方法 2、券&#xff08;记得点个关注&#xff0c;做一下数据&#xff09;

Node学习笔记之MongoDB

一、简介 1.1 Mongodb 是什么 MongoDB 是一个基于分布式文件存储的数据库&#xff0c;官方地址 MongoDB: The Developer Data Platform | MongoDB 1.2 为什么选择 Mongodb 操作语法与 JavaScript 类似&#xff0c;容易上手&#xff0c;学习成本低 二、核心概念 Mongodb 中…