优先级队列详解

news2024/11/25 7:16:58

目录

  • 优先级队列简介
  • 关于堆
    • 为什么得用完全二叉树
  • 用堆来实现优先级队列
    • 插入/删除/获取优先级最高的元素
    • 模拟实现
  • 使用PriorityQueue的注意事项
  • PriorityQueue常用接口
    • 优先级队列的构造方法

优先级队列简介

PriorityQueue,即优先级队列。它可以保证每次出出来的数据是队列中最大或最小的元素。
JDK1.8中的PriorityQueue底层使用了堆这种数据结构。

关于堆

堆实际就是在完全二叉树的基础上进行了一些调整。

堆的性质:
堆中某个节点的值总是不大于或不小于其父节点的值;
堆总是一棵完全二叉树。

为什么得用完全二叉树

我们来看下图就明白了:
在这里插入图片描述首先我们得明白,堆中数据的存储是用数组来实现的,那么它们的存储结构就出来了:
在这里插入图片描述
我们可以看到对于完全二叉树,它可以做到不浪费空间,而非完全二叉树可能会产生很多零碎的空间浪费了,因此我们采用完全二叉树.

将元素存储到数组中后,可以根据二叉树的性质对树进行还原。
假设 i 为节点在数组中的下标,则有:
如果i为0,则i表示的节点为根节点,否则i节点的双亲节点为 (i - 1)/2
如果2 * i + 1 小于总的节点个数,则节点i的左孩子下标为2 * i + 1,否则没有左孩子
如果2 * i + 2 小于总的节点个数,则节点i的右孩子下标为2 * i + 2,否则没有右孩子

用堆来实现优先级队列

先来看看优先级队列的常用方法

插入/删除/获取优先级最高的元素

  1. boolean offer(E e)
    插入元素e,插入成功返回true,如果e对象为空,抛出NullPointerException异常,时间复杂度 ,注意:空间不够时候会进行扩容
  2. E peek()
    获取优先级最高的元素,如果优先级队列为空,返回null
  3. E poll()
    移除优先级最高的元素并返回,如果优先级队列为空,返回null
  4. int size()
    获取有效元素的个数
  5. void clear()
    清空
  6. boolean isEmpty()
    检测优先级队列是否为空,空返回true

模拟实现

public class PriorityQueue {
    public int[] elem;
    public int usedSize;  //记录元素个数
 
    public PriorityQueue() {
        elem = new int[11];  //数组初始大小为11
        usedSize = 0;
    }
 
    /**
     * 建堆的时间复杂度:O(n*logn)
     *
     * @param array
     */
    public void createHeap(int[] array) {   //将该数组变为优先级队列
        elem = array;
        usedSize = array.length;
        for(int i = (usedSize-2)/2; i >= 0; i--) {  //从最后一个元素的父节点开始依次向下调整
            shiftDown(i,usedSize);
        }
    }
 
    /**
     *
     * @param root 是每棵子树的根节点的下标
     * @param len  是每棵子树调整结束的结束条件
     * 向下调整的时间复杂度:O(logn)
     */
    private void shiftDown(int root,int len) {
        int a = root*2 + 1;   //a表示左边的子节点
        while(a < len) {    //向下调整直到a越界了
            if(a+1 < len && elem[a] < elem[a+1]) {  //如果root有右节点,并且右节点大于左节点,则令a等于右节点
                a = a+1;    //这里a就表示root的最大子节点
            }
            if(elem[root] < elem[a]) {  //如果最大子节点大于root节点,则两者交换
                int tmp = elem[a];
                elem[a] = elem[root];
                elem[root] = tmp;
                root = a;   //将root指向交换了的子节点
                a = root*2 + 1;  //a重新指向root的左子节的
            }else {
                break;   //不大于直接退出循环
            }
        }
    }
    
    /**
     * 入队:仍然要保持是大根堆
     * @param val
     */
    public void push(int val) {
        if(isFull()) {  //如果满了扩容
            elem = Arrays.copyOf(elem,2*elem.length);
        }
        elem[usedSize++] = val;  //将待添加的元素放在数组最后面
        shiftUp(usedSize-1);  //将添加的元素进行向上调整
    }
 
    private void shiftUp(int child) {
        int root = (child-1) / 2;  //找出child的父节点root
        while(root >= 0) {   
            if(elem[child] > elem[root]) {  //如果子节点大于父节点,则交换
                int tmp = elem[child];
                elem[child] = elem[root];
                elem[root] = tmp;
                child = root;   //将子节点指向父节点
                root = (child-1) / 2;  
            }else {
                break;
            }
        }
    }
 
    public boolean isFull() {
        return usedSize == elem.length;
    }
 
    /**
     * 出队【删除】:每次删除的都是优先级高的元素
     * 仍然要保持是大根堆
     */
    public void pollHeap() {
        if(isEmpty()) {
            throw new RuntimeException("队列为空,无法删除元素");
        }
        elem[0] = elem[--usedSize];  //删除其实就是将最后一个元素覆盖第一个元素
        shiftDown(0,usedSize);  //然后对第一个元素进行向下调整
    }
 
    public boolean isEmpty() {
        return usedSize == 0;
    }
 
    /**
     * 获取堆顶元素
     * @return
     */
    public int peekHeap() {
        return elem[0];
    }
}

这里实现的是大根堆, 其实PriorityQueue底层是小根堆.

使用PriorityQueue的注意事项

  1. 使用时必须导入PriorityQueue所在的包,即:
import java.util.PriorityQueue;
  1. PriorityQueue中放置的元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出
    ClassCastException异常.

  2. 不能插入null对象,否则会抛出NullPointerException
    在这里插入图片描述

  3. 插入元素时, 若空间不足, 其内部可以自动扩容

在这里插入图片描述
可以看到, 如果原数组大小小于64, 则二倍扩容, 大于则1.5倍扩容.
在这里插入图片描述
可以看到扩容最大为 Integer.MAX_VALUE 也就是2^31 - 1.
在这里插入图片描述

  1. 插入和删除元素的时间复杂度为: O(logN)
  2. PriorityQueue底层使用了堆数据结构
  3. PriorityQueue默认情况下是小堆—即每次获取到的元素都是最小的元素.

PriorityQueue常用接口

优先级队列的构造方法

可以看到有很多构造方法:
在这里插入图片描述

这里介绍几个我们常用的构造方法:
在这里插入图片描述
默认情况下,PriorityQueue队列是小堆,如果需要大堆需要用户提供比较器.
用到这个构造方法:
在这里插入图片描述

	// 用户自己定义的比较器:直接实现Comparator接口,然后重写该接口中的compare方法即可
class IntCmp implements Comparator<Integer>{
	@Override
	public int compare(Integer o1, Integer o2) {
		return o2-o1;  //这里如果是o1-o2则是小根堆
	}
}
public class TestPriorityQueue {
	public static void main(String[] args) {
		PriorityQueue<Integer> p = new PriorityQueue<>(new IntCmp());
			p.offer(4);
			p.offer(3);
			p.offer(2);
			p.offer(1);
			p.offer(5);
			System.out.println(p.peek());
		}
}

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

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

相关文章

探究:kafka生产者/消费者与多线程安全

目录 1. 多线程安全 1.1. 生产者是多线程安全的么&#xff1f; 1.1. 消费者是多线程安全的么&#xff1f; 2. 消费者规避多线程安全方案 2.1. 每个线程维护一个kafkaConsumer 2.2. [单/多]kafkaConsumer实例 多worker线程 2.3.方案优缺点对比 1. 多线程安全 1.1. 生产…

我的Git stash不小心清空了怎么办,提了代码能反悔吗

文章目录1. 前言2. git stash清空场景2. git stash clear后如何还原3.Git撤销已经推送(push)至远端仓库的信息1. 前言 本文总结的知识很实用哈&#xff0c;虽然是git工具的不常用操作&#xff0c;但是绝对不是冷知识&#xff0c;学会可以从会用git升级到git高手。 主要是两种场…

Centos7 安装Mysql8.0

1、到指定目录下下载安装包[rootVM-0-14-centos ~]# cd /usr/local/src2、下载mysql8[rootVM-0-14-centos src]# wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.20-linux-glibc2.12-x86_64.tar.xz3、解压mysql8, 通过xz命令解压出tar包&#xff0c; 然后通过t…

KDZD耐电压高压击穿强度测试仪

一、技术参数 01、输入电压&#xff1a; 交流 220 V。 02、输出电压&#xff1a; 交流 0--50KV ; 直流 0—50kv 。 03、电器容量&#xff1a;3KVA。 04、高压分级&#xff1a;0—50KV&#xff0c;&#xff08;全程可调&#xff09;。 05、升压速率&#xff1a;0.1KV/s-…

c++11 标准模板(STL)(std::unordered_map)(八)

定义于头文件 <unordered_map> template< class Key, class T, class Hash std::hash<Key>, class KeyEqual std::equal_to<Key>, class Allocator std::allocator< std::pair<const Key, T> > > class unordered…

【C++】你不得不爱的——继承

凡是面向对象的语言&#xff0c;都有三大特性&#xff0c;继承&#xff0c;封装和多态&#xff0c;但并不是只有这三个特性&#xff0c;是因为者三个特性是最重要的特性&#xff0c;那今天我们一起来看继承&#xff01; 目录 1.继承的概念及定义 1.概念 2.继承的定义 2.基类…

Linux进程学习【进程地址】

✨个人主页&#xff1a; Yohifo &#x1f389;所属专栏&#xff1a; Linux学习之旅 &#x1f38a;每篇一句&#xff1a; 图片来源 &#x1f383;操作环境&#xff1a; CentOS 7.6 阿里云远程服务器 Perseverance is not a long race; it is many short races one after another…

Dynabook笔记本电脑无法开机怎么重装新系统?

Dynabook笔记本电脑无法开机怎么重装新系统&#xff1f;有用户使用Dynabook笔记本电脑出现了无法正常开机的情况。遇到这样的问题是我们的电脑系统出现了损坏&#xff0c;可以尝试进行系统修复。如果无法修复的话&#xff0c;就需要进行系统重装了。以下为大家带来Dynabook笔记…

SQLMap安装教程

注意&#xff1a;在python3环境下安装sqlmap的时候会提示需要在python2的环境下才能安装&#xff0c;其实在python3.6以后也都支持sqlmap了。 sqlmap安装步骤&#xff1a; 一、下载python&#xff1b; 下载地址 https://www.python.org/downloads/ 下载教程参考&#xff08…

通过反射获取注解的属性值(内含源代码)

通过反射获取注解的属性值&#xff08;内含源代码&#xff09; 源代码下载链接地址&#xff1a;https://download.csdn.net/download/weixin_46411355/87554543 目录通过反射获取注解的属性值&#xff08;内含源代码&#xff09;源代码下载链接地址&#xff1a;[https://downl…

做互联网自媒体创业的月薪收入真的能过万吗?

搞自媒体创业有前途吗&#xff1f;收入月薪过万是真的吗&#xff1f; 自媒体创业是一种新兴的创业方法&#xff0c;它的远景十分广阔。自媒体创业能够让人们在自己的兴趣爱好和专业范畴上发挥自己的才能&#xff0c;一起也能够获得不错的收入。可是&#xff0c;月薪过万并不是…

ArangoDB——AQL编辑器

AQL 编辑器 ArangoDB 的查询语言称为 AQL。AQL与关系数据库管理系统 (RDBMS)区别在于其更像一种编程语言&#xff0c;更自然地适合无模式模型&#xff0c;并使查询语言非常强大&#xff0c;同时保持易于读写。数据建模概念 数据库是集合的集合。集合存储记录&#xff0c;称为文…

三维人脸实践:基于Face3D的人脸生成、渲染与三维重建 <三>

face3d: Python tools for processing 3D face git code: https://github.com/yfeng95/face3d paper list: PaperWithCode 基于BFM模型&#xff0c;估计3DMM的参数&#xff0c;可以实现线性的人脸表征&#xff0c;该方法可用于基于关键点的人脸生成、位姿检测以及渲染等。推荐…

信息收集之搜索引擎

Google Hacking 也可以用百度&#xff0c;不过谷歌的搜索引擎更强大 site 功能&#xff1a;搜索指定域名的网页内容&#xff0c;可以用来搜索子域名、跟此域名相关的内容 示例&#xff1a; site:zhihu.com 搜索跟zhihu.com相关的网页“web安全” site:zhihu.com 搜索zhihu…

提升学习 Prompt 总结

NLP现有的四个阶段&#xff1a; 完全有监督机器学习完全有监督深度学习预训练&#xff1a;预训练 -> 微调 -> 预测提示学习&#xff1a;预训练 -> 提示 -> 预测 阶段1&#xff0c;word的本质是特征&#xff0c;即特征的选取、衍生、侧重上的针对性工程。 阶段2&…

C++核心编程

一、内存分区模型概述&#xff1a;C程序在执行时&#xff0c;将内存划分为4个区域程序运行前&#xff1a;代码区&#xff1a;存放函数体的二进制代码&#xff0c;由操作系统管理①共享。共享的目的是对于频繁被执行的程序&#xff0c;在内存中只需有一份代码即可②只读。使其只…

组合预测 | MATLAB实现EMD-KPCA-LSTM、EMD-LSTM、LSTM多输入单输出回归预测对比

组合预测 | MATLAB实现EMD-KPCA-LSTM、EMD-LSTM、LSTM多输入单输出回归预测对比 目录 组合预测 | MATLAB实现EMD-KPCA-LSTM、EMD-LSTM、LSTM多输入单输出回归预测对比预测效果基本介绍模型描述程序设计参考资料预测效果 基本介绍 MATLAB实现EMD-KP

传输层协议 TCP UDP

目录 协议前菜 端口号 ​编辑端口号范围划分 认识知名端口号(Well-Know Port Number) netstat pidof 传输层协议 UDP协议 UDP协议端格式 UDP的特点 面向数据报 UDP的缓冲区 UDP使用注意事项 基于UDP的应用层协议 TCP协议 TCP协议概念 TCP协议段格式 标志…

深度分析中国高端投教市场第一股“九方财富”的投资价值

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 猛兽财经获悉&#xff0c;九方财富&#xff08;09636&#xff09;已于3月10在港交所成功IPO上市&#xff0c;并成为了“中国在线高端投教市场第一股”。 作为中国领先的在线投资决策方案提供商&#xff0c;九方财富…

一起来学习配置Combo接口吧!

Combo接口是一个光电复用的逻辑接口&#xff0c;一个Combo接口对应设备面板上一个GE电接口和一个GE光接口。电接口与其对应的光接口是光电复用关系&#xff0c;两者不能同时工作&#xff08;当激活其中一个接口时&#xff0c;另一个接口就自动处于禁用状态&#xff09;&#xf…