【Java--数据结构】优先级队列( PriorityQueue)

news2025/3/14 16:29:50

一. 优先级队列

1.1  优先级队列的概念

优先级队列是一种特殊的队列,它在入队时会根据元素的优先级进行排序,优先级最高的元素排在队列的前面,出队时会优先出队优先级最高的元素。

1.2 优先级队列的区别

(1)与普通队列的区别

  1. 普通队列是先进先出的,元素按照入队的顺序依次出队。
  2. 优先级队列不考虑入队的先后顺序,只根据元素的优先级来决定出队顺序

(2)与栈的区别

  1. 栈是后进先出的,最后入栈的元素最先出栈。
  2. 优先级队列则根据优先级决定出队顺序,与入队顺序无关。

核心特点元素按优先级排序,每次取出优先级最高(或最低)的元素 

二. 堆

PriorityQueue类底层使用堆这种数据结构,堆适用于完全二叉树

2.1 堆的概念

是一种特殊的完全二叉树数据结构

完全二叉树:除了最后一层外,每一层的节点数都是满的,并且最后一层的节点都靠左排列。

堆的分类:

(1)最大堆:父节点 ≥ 子节点,根节点为全局最大值。

(2)最小堆:父节点 ≤ 子节点,根节点为全局最小值。

2.2 堆的性质

(1)堆序性:

  1. 根节点是堆中的最大元素(大根堆)或最小元素(小根堆)
  • 比如:在大根堆中,根节点(第一层)值大于等于第二层子节点的值,第二层子节点的值又大于等于第三层子节点的值,以此类推。

(2)完全二叉树:

  1. 所有层(除最后一层)必须完全填满

  2. 最后一层的节点必须从左到右连续,不能出现中间空缺

注意:

节点关系下标计算公式
父节点parent = (i-1)/2
左子节点left = 2*i + 1
右子节点right = 2*i + 2

2.3 堆的操作

(1)向下调整

常用于创建堆和删除操作

主要步骤:

循环条件:子节点的下标小于总数

  1. 根据父亲节点找到字节点
  2. 比较得出左右节点的最大节点
  3. 比较父节点和最大节点,如果左右最大节点大于父节点,则交换
  4. 父亲节点等于字节点

代码实现:

    //向下调整
    public void shiftDown(int i ,int usedSize){
        int child = (i*2)+1;//左孩子
        while (child<usedSize) {
            if (child + 1 < usedSize && elem[child] < elem[child + 1]) {
                child++;
            }
            if (elem[child] > elem[i]) {
                swap(child,i);
                i = child;
                child = (i*2)+1;
            }else {
                break;
            }
        }
    }

(2)向上调整

常用于增加操作

 主要步骤:

循环条件:子节点的下标大于0

  1. 找出子节点的父亲节点
  2. 与父亲节点进行大小比较,大于则交换
  3. 让子节点等于父亲

 代码实现:

    //向上调整
    public void shiftUp(int child){
        int parent = (child-1)/2;
        while (child>0) {
            if (elem[parent] < elem[child]) {
                swap(parent, child);
                child = parent;
                parent = (child - 1) / 2;
            } else {
                break;
            }
        }
    }

(3)堆的创建(最大堆)

从最后一个非叶子节点开始,自底向上逐个向下调整

主要步骤:

  1. 得到最大下标的父亲节点(最后一个非叶子节点)
  2. 向下调整

代码实现: 

 public void createHeap(){
        //找出最大的根节点
        for (int i = (usedSize-1-1)/2; i >=0 ; i--) {
            shiftDown(i,usedSize);
        }
    }
    //向下调整
    public void shiftDown(int i ,int usedSize){
        int child = (i*2)+1;//左孩子
        while (child<usedSize) {
            if (child + 1 < usedSize && elem[child] < elem[child + 1]) {
                child++;
            }
            if (elem[child] > elem[i]) {
                swap(child,i);
                i = child;
                child = (i*2)+1;
            }else {
                break;
            }
        }
    }

(4)堆的插入

主要步骤:

  1. 将插入的元素放入最后一位
  2. 向上调整
  3. 堆的元素个数加一个

代码实现:

    public void offer(int val){
        if(isFull()){
            this.elem = Arrays.copyOf(elem,elem.length+1);
        }else {
            elem[usedSize] = val;
            shiftUp(usedSize);
            usedSize++;
        }
    }

注意:必须是在已经有序的基础上插入 

(5)堆的删除

堆的删除是删除栈顶元素

主要步骤:

  1. 将栈顶元素和最后一个元素进行交换
  2. 堆中的元素总数减少一个
  3. 栈顶元素向下调整

 代码实现:

    public int poll(){
        int tmp = elem[0];
        swap(0,usedSize-1);
        usedSize--;
        shiftDown(0,usedSize);
        return tmp;
    }

(6)堆的排序

最大堆——由小到大

主要步骤:

  1. 交换堆顶与末尾元素
  2. 调整剩余堆
  3. 重复直至有序

代码实现:

    //排序从小 到大
    public void heapSort(){
        int end = usedSize-1;
        while(end>0){
            swap(0,end);
            shiftDown(0,end);
            end--;
        }
    }

三. PriorityQueue

3.1 PriorityQueue的特性

 注意:

  1. PriorityQueue底层使用堆数据结构
  2. 默认情况下生成的是最下堆,如果想要生成最大堆可以通过使用比较器
  3. 时间复杂度:插入和删除O(log2n)。
  4. 堆中不能存放 null对象
  5. 堆中存放的元素必须要可以比较
  6. 使用时要导入PriorityQueue所在的包

3.2 PriorityQueue的使用

1. 构造方法

(1) 无参构造

Priority( )

        PriorityQueue<Integer> queue = new PriorityQueue<>();

 注意:无参构造方法,没有给定大小,默认容量为11

(2) 有参构造

PriorityQueue(int initialCapacity)

PriorityQueue<Integer> queue1 = new PriorityQueue<>(100);

注意:在调用有参构造器时,传入的参数不能小于1,不然会报错

(3) 利用Collection构建

PriorityQueue(Collection <? extends E>c)

import java.util.PriorityQueue;

public class Text_1 {
    public static void main(String[] args) {
        PriorityQueue<Integer> queue = new PriorityQueue<>();

        PriorityQueue<Integer> queue1 = new PriorityQueue<>(100);
        queue1.offer(12);
        queue1.offer(3);
        queue1.offer(9);

        PriorityQueue<Integer> queue2 =new PriorityQueue<>(queue1);
        queue2.offer(999);
        System.out.println(queue2);

    }
}
//输出:[3, 12, 9, 999]

注意:

在使用PriorityQueue类的时候,不要忘记导入包 

2. 基本操作

(1)插入

插入元素,插入成功返回true,如果插入内容为空,抛出异常

import java.util.PriorityQueue;

public class Offer {
    public static void main(String[] args) {
        PriorityQueue<Integer> queue = new PriorityQueue<>();
        queue.offer(14);
        queue.offer(2);
        queue.offer(7);
        System.out.println(queue);
    }
}

注意: 空间不够时候会自动进行扩容

(2)删除

移除优先级最高的元素并返回,如果优先级队列为空,返回null

import java.util.PriorityQueue;

public class Poll {
    public static void main(String[] args) {
        PriorityQueue<Integer> queue = new PriorityQueue<>();
        queue.offer(14);
        queue.offer(2);
        queue.offer(7);
        int x = queue.poll();
        System.out.println(x);
    }
}
(3)获取

获取优先级最高的元素,如果优先级队列为空,返回null

import java.util.PriorityQueue;

public class Peek {
    public static void main(String[] args) {
        PriorityQueue<Integer> queue = new PriorityQueue<>();
        queue.offer(14);
        queue.offer(2);
        queue.offer(7);
        int x = queue.peek();
        System.out.println(x);
    }
}
(4)大小

获取有效元素的个数

import java.util.PriorityQueue;

public class Size {
    public static void main(String[] args) {
        PriorityQueue<Integer> queue = new PriorityQueue<>();
        queue.offer(14);
        queue.offer(2);
        queue.offer(7);
        int x = queue.size();
        System.out.println(x);
    }
}
(5)清空
import java.util.PriorityQueue;

public class IsEmpty {
    public static void main(String[] args) {
        PriorityQueue<Integer> queue = new PriorityQueue<>();
        queue.offer(14);
        queue.offer(2);
        queue.offer(7);
        boolean x = queue.isEmpty();
        System.out.println(x);
        queue.clear();
        boolean x1 = queue.isEmpty();
        System.out.println(x1);
    }
}
(6)判空

检测优先级队列是否为空,空返回true

import java.util.PriorityQueue;

public class IsEmpty {
    public static void main(String[] args) {
        PriorityQueue<Integer> queue = new PriorityQueue<>();
        queue.offer(14);
        queue.offer(2);
        queue.offer(7);
        boolean x = queue.isEmpty();
        System.out.println(x);
        queue.clear();
        boolean x1 = queue.isEmpty();
        System.out.println(x1);
    }
}

3.3 练习

top-k问 题:最大或者最小的前k个数据

求前k最大元素--使用最小堆

求前k最小元素--使用最大堆

(1)求前k个最大元素

方法1:使用最大堆(不推荐,如果数据太多,时间复杂度会很大,很占用内存)

主要步骤:

  1. 将数组内所有元素放入优先级队列中
  2. 每次删除队列中的最大值
  3. 将最大值赋值给新的数组内
    public int[] maxKK(int[] arr, int k) {
        int[] ret = new int[k];
        if(arr==null||k<=0){
            return ret;
        }
        //默认最小堆,通过使用比较器,变成默认最大堆
        PriorityQueue<Integer> queue = new PriorityQueue<>(arr.length);
        for (int i = 0; i < arr.length; i++) {
            queue.offer(arr[i]);
        }
        //每次弹出的就是最大的
        for (int i = 0; i < k; i++) {
            ret[i] = queue.poll();
        }
        return ret;
    }

方法2: 使用最小堆

刷新每一次下限(最小值)

主要步骤:

  1. 先将数组内的前k个元素放进优先级队列里
  2. 从第k个元素往后开始逐个比较,如果k下标的值比队列的最小值大,那就删除最小元素,添加这个元素
  3. 每次都去掉最小的元素,那么就剩下了最大的
    //找出最大的k个数
    public int[] maxK(int[] array,int k){
        int[] ret = new int[k];
        if(k<=0||array == null){
            return ret;
        }
        PriorityQueue<Integer> queue = new PriorityQueue<>(k);
        for (int i = 0; i < k; i++) {
            queue.offer(array[i]);
        }

        for (int i = k; i < array.length; i++) {
            int x = queue.peek();
            if(array[i]>x){
                queue.poll();
                queue.offer(array[i]);
            }
        }
        for (int i = 0; i < k; i++) {
            ret[i] = queue.poll();
        }

        return ret;
    }
(2)求前k个最小元素

使用最大堆

刷新每一次上限(最大值)

主要步骤:

  1. 先将数组内的前k个元素放进优先级队列里
  2. 从第k个元素往后开始逐个比较,如果k下标的值比队列的最大值小,那就删除最大元素,添加这个元素
  3. 每次都去掉最大的元素,那么就剩下了最小的
 public int[] MinK(int[] array,int k){
        int[] ret = new int[k];
        if(k<=0||array == null){
            return ret;
        }

        PriorityQueue<Integer> queue = new PriorityQueue<>(new IntCmp());
        //将数组的前k个元素入队列
        for (int i = 0; i < k; i++) {
            queue.offer(array[i]);
        }

        for (int i = k; i < array.length; i++) {
            //队列中的最大值
            int x = queue.peek();
            //将最大的弹出去,将这个小的放进来
            if(array[i] < x){
                queue.poll();
                queue.offer(array[i]);
            }
        }
        //将队列中的最大值依次放入数组
        for (int i = 0; i < k; i++) {
            ret[i] = queue.poll();
        }

        return ret;
    }

点赞的宝子今晚自动触发「躺赢锦鲤」buff!

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

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

相关文章

【 <一> 炼丹初探:JavaWeb 的起源与基础】之 JavaWeb 项目的部署:从开发环境到生产环境

<前文回顾> 点击此处查看 合集 https://blog.csdn.net/foyodesigner/category_12907601.html?fromshareblogcolumn&sharetypeblogcolumn&sharerId12907601&sharereferPC&sharesourceFoyoDesigner&sharefromfrom_link <今日更新> 一、开发环境…

【AIGC】OpenAI 集成 Langchain 操作实战使用详解

目录 一、前言 二、前置准备 2.1 安装 Langchain必须的依赖 2.1.1 python环境 2.1.2 langchain openai 环境 2.1.3 准备一个apikey 2.1.4 langchain 核心组件 三、Langchain 各组件使用 3.1 Chat models组件 3.1.1 Invocation 使用 3.1.1.1 结果解析 3.2 提示词模板…

Xxl-Job学习笔记

目录 概述 核心架构 核心特点 应用场景 什么是任务调度 快速入门 获取源码 初始化调度数据库 基本配置 数据源datasource 邮箱email&#xff08;可选&#xff09; 会话令牌access token 启动调度中心 启动执行器 依赖 yaml基本配置 XxlJobConfig类配置 定义执…

SAIL-RK3576核心板应用方案——无人机视觉定位与地面无人设备通信控制方案

本方案以 EFISH-RK3576-SBC工控板 或 SAIL-RK3576核心板 为核心&#xff0c;结合高精度视觉定位、实时通信与智能控制技术&#xff0c;实现无人机与地面无人设备的协同作业。方案适用于物流巡检、农业植保、应急救援等场景&#xff0c;具备高精度定位、低延迟通信与强环境适应性…

CSS 入门指南(一):基本概念 选择器 常用元素属性

一、初识 CSS 1, CSS 定义 层叠样式表(Cascading Style Sheets&#xff0c;缩写为 CSS)&#xff0c;是一种 样式表 语言&#xff0c;用来描述 HTML 文档的呈现&#xff08;美化内容&#xff09; CSS 能够对网页中元素位置的排版进行 像素级 精确控制&#xff0c;实现美化页面…

HTML5(Web前端开发笔记第一期)

p.s.这是萌新自己自学总结的笔记&#xff0c;如果想学习得更透彻的话还是请去看大佬的讲解 目录 三件套标签标题标签段落标签文本格式化标签图像标签超链接标签锚点链接默认链接地址 音频标签视频标签 HTML基本骨架综合案例->个人简介列表表格表单input标签单选框radio上传…

【AIGC】计算机视觉-YOLO系列家族

YOLO系列家族 &#xff08;1&#xff09;YOLO发展史&#xff08;2&#xff09; YOLOX&#xff08;3&#xff09; YOLOv6&#xff08;4&#xff09; YOLOv7&#xff08;5&#xff09; YOLOv8&#xff08;6&#xff09; YOLOv9&#xff08;7&#xff09;YOLOv10&#xff08;8&…

The First项目报告:重塑 DeFi 流动性的革新者,ELX 即将登陆 The First

随着去中心化金融&#xff08;DeFi&#xff09;的持续发展&#xff0c;流动性问题一直是各类去中心化交易所&#xff08;DEX&#xff09;和项目方面临的核心挑战。传统的做市模式往往需要依赖中心化流动性提供者&#xff0c;而这些机构的资金控制能力可能影响代币价格波动&…

OpenCV连续数字识别—可运行验证

前言 ​ 文章开始&#xff0c;瞎说一点其他的东西&#xff0c;真的是很离谱&#xff0c;找了至少两三个小时&#xff0c;就一个简单的需求&#xff1a; 1、利用OpenCV 在Windows进行抓图 2、利用OpenCV 进行连续数字的检测。 3、使用C&#xff0c;Qt 3、将检测的结果显示出来 …

LiveGBS流媒体平台GB/T28181功能-海康大华宇视华为像头GB28181国标语音对讲语音喊话需要的摄像头设备及服务HTTPS准备

LiveGBS海康大华宇视华为像头GB28181国标语音对讲语音喊话需要的摄像头设备及服务HTTPS准备 1、背景2、准备2.1、服务端必备条件&#xff08;注意&#xff09;2.2、准备语音对讲设备2.2.1、 大华摄像机2.2.1.1、 配置接入示例2.2.1.2、 配置音频通道编号 2.2.2、 海康摄像机2.2…

第十五章:go package 包的管理

import f "fmt"   // 注意 这里 f 是包的别名 init初始化函数 在每一个Go源文件中&#xff0c;都可以定义任意个如下格式的特殊函数&#xff1a; func init(){// ... } package&#xff1a;声明包的关键字 packagename&#xff1a;包名&#xff0c;可以不与文…

deepseek的regflow安装mac版本

deepseek的ragflow部署安装 一:ollama安装,自行完成,我本地已安装 二:查看大模型情况oll::命令ollama list,我本地无ragflow 三:docker安装:命令docker version ,自行完成,我本地已安装 四:安装知识库软件ragflow: 简单科普下Ragflow 是一个基于深度学习模型的问答生成工具&…

文献分享: 对ColBERT段落多向量的剪枝——基于学习的方法

原论文 1. 导论 & \textbf{\&} &方法 1️⃣要干啥&#xff1a;在 ColBERT \text{ColBERT} ColBERT方法中&#xff0c;限制每个段落要保留的 Token \text{Token} Token的数量&#xff0c;或者说对段落 Token \text{Token} Token进行剪枝 2️⃣怎么干&#xff1a;注…

社交软件频繁更新,UI 设计在其中扮演什么角色?

在当今数字化时代&#xff0c;社交软件已成为人们日常生活中不可或缺的一部分。随着科技的飞速发展和用户需求的不断变化&#xff0c;社交软件更新频率日益加快。在这频繁更新的背后&#xff0c;UI 设计扮演着至关重要的角色&#xff0c;它如同社交软件的 “门面担当” 与 “交…

Gemini Robotics:Google DeepMind 让 AI 机器人真正“动”起来!

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

概率论的基本知识

逆概率还不懂&#xff0c;改天再想想。 联合概率 联合概率&#xff08;Joint Probability&#xff09; 是概率论中的一个重要概念&#xff0c;用于描述多个随机变量同时取某些值的概率。联合概率可以帮助我们理解多个变量之间的关系。

AI大数据挖掘的威力

通过AI挖掘大数据可以发现很多世界上用传统方法无法发现的潜在规律。 人类群体可以被精准的操控&#xff0c;这在AI发达的未来会越来越现实&#xff0c;甚至可以在社会动荡前夕精准清理权威节点。 基于AI与大数据的人类群体潜在规律发现 随着AI和大数据技术的深度结合&#xf…

使用服务器如何DNS呢

莱卡云服务器 DNS 配置指南 一、配置云服务器本地 DNS ‌修改网络配置文件‌ ‌Ubuntu/Debian‌&#xff1a; bashCopy Code sudo nano /etc/network/interfaces # 添加或修改 DNS 配置 dns-nameservers 8.8.8.8 8.8.4.4 *&#xff08;保存后重启网络服务&#xf…

【SpringBoot】实现登录功能

在上一篇博客中&#xff0c;我们讲解了注册页面的实现。在此基础上会跳转到登录页面&#xff0c;今天给大家带来的是使用 SpringBoot&#xff0c;MyBatis&#xff0c;Html&#xff0c;CSS&#xff0c;JavaScript&#xff0c;前后端交互实现一个登录功能。 目录 一、效果 二、…

图论part3|101.孤岛的总面积、沉没孤岛、417. 太平洋大西洋水流问题

101. 孤岛的总面积 &#x1f517;&#xff1a;101. 孤岛的总面积思路&#xff1a;和昨天的岛的区别是&#xff1a;是否有挨着边的岛屿 所以可以先遍历四条边挨着的岛屿&#xff0c;把他们标记为非孤岛再计算其他岛屿当中的最大面积 代码&#xff1a;&#xff08;深度搜索&…