PriorityQueue 解析

【数据结构】了解PriorityQueue,优先队列   PriorityQueue又称为优先队列,从名字我们就能知道,他有队列的特性,先进先出,但又不完全是队列,因为他是优先队列,所以更严谨的说应该是先进优出。   优出的话,那肯定就不是一个简单的队列那么简单了,以为队列是没有排序的,当然我们最先想到的是树的模型,可以给数据进行排序,但是树的模型不能让我们随时随地的取出最优的哪一个,所以拓展一下就有了堆。所以我们自然而然可以想到PriorityQueue的底层其实是一个堆,在Java里面是最小堆。   所以,在学PriorityQueue之前,我们必须明白最 阅读详情

Java 1.5版本后就提供了一个具备了小根堆性质的数据结构也就是优先队列PriorityQueue。

//PriorityQueue默认是一个小顶堆,然而可以通过传入自定义的Comparator函数来实现大顶堆。

实际上是一个堆(不指定Comparator时默认为最小堆)
队列既可以根据元素的自然顺序来排序,也可以根据 Comparator来设置排序规则。
队列的头是按指定排序方式的最小元素。如果多个元素都是最小值,则头是其中一个元素。
新建对象的时候可以指定一个初始容量,其容量会自动增加。

注意1:该队列是用数组实现,但是数组大小可以动态增加,容量无限。

注意2:队列的实现不是同步的。不是线程安全的。如果多个线程中的任意线程从结构上修改了列表, 则这些线程不应同时访问 PriorityQueue实例。保证线程安全可以使用PriorityBlockingQueue 类。

注意3:不允许使用 null 元素。

注意4:插入方法(offer()、poll()、remove() 、add() 方法)时间复杂度为O(log(n)) ;
remove(Object) 和 contains(Object) 时间复杂度为O(n);
检索方法(peek、element 和 size)时间复杂度为常量。

注意5:方法iterator()中提供的迭代器并不保证以有序的方式遍历优先级队列中的元素。(原因可参考PriorityQueue的内部实现)
如果需要按顺序遍历,可用Arrays.sort(pq.toArray())。

注意6:可以在构造函数中指定如何排序。如:
PriorityQueue()
          使用默认的初始容量(11)创建一个 PriorityQueue,并根据其自然顺序来排序其元素(使用 Comparable)。
PriorityQueue(int initialCapacity)
          使用指定的初始容量创建一个 PriorityQueue,并根据其自然顺序来排序其元素(使用 Comparable)。
PriorityQueue(int initialCapacity, Comparator<? super E> comparator)
          使用指定的初始容量创建一个 PriorityQueue,并根据指定的比较器comparator来排序其元素。

注意7:此类及其迭代器实现了 Collection 和 Iterator 接口的所有可选 方法。

PriorityQueue对元素采用的是堆排序,头是按指定排序方式的最小元素。堆排序只能保证根是最大(最小),整个堆并不是有序的。
方法iterator()中提供的迭代器可能只是对整个数组的依次遍历。也就只能保证数组的第一个元素是最小的。

 

//PriorityQueue默认是一个小顶堆,然而可以通过传入自定义的Comparator函数来实现大顶堆

PriorityQueue的数据结构

PriorityQueue的逻辑结构是一棵完全二叉树,存储结构其实是一个数组。逻辑结构层次遍历的结果刚好是一个数组。

PriorityQueue的操作


add(E e) 和 offer(E e) 方法

add(E e) 和 offer(E e) 方法都是向PriorityQueue中加入一个元素,其中add()其实调用了offer()方法如下:

public boolean add(E e) {
        return offer(e);
    }
下面主要看看offer()方法的作用: 

这里写图片描述

如上图调用 offer(4)方法后,往堆中压入4然后从下往上调整堆为小顶堆。offer()的代码实现:

public boolean offer(E e) {
        if (e == null)
            throw new NullPointerException();
      //如果压入的元素为null 抛出异常      
        int i = size;
        if (i >= queue.length)
            grow(i + 1);
            //如果数组的大小不够扩充
        size = i + 1;
        if (i == 0)
            queue[0] = e;
            //如果只有一个元素之间放在堆顶
        else
            siftUp(i, e);
            //否则调用siftUp函数从下往上调整堆。
        return true;
    }

private void siftUp(int k, E x) {
        if (comparator != null)
            siftUpUsingComparator(k, x);
        else
            siftUpComparable(k, x);
    }


当前元素与父节点不断比较如果比父节点小就交换然后继续向上比较,否则停止比较的过程。
private void siftUpComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>) x;
        while (k > 0) {
            int parent = (k - 1) >>> 1;
            Object e = queue[parent];
            if (key.compareTo((E) e) >= 0)
                break;
            queue[k] = e;//被比较的节点向父节点移动
            k = parent;//保存当前位置
        }
        queue[k] = key;
    }

poll() 和 remove() 方法 


poll 方法每次从 PriorityQueue 的头部删除一个节点,也就是从小顶堆的堆顶删除一个节点,而remove()不仅可以删除头节点而且还可以用 remove(Object o) 来删除堆中的与给定对象相同的最先出现的对象。先看看poll()方法。下面是poll()之后堆的操作

删除元素后要对堆进行调整: 

这里写图片描述

堆中每次删除只能删除头节点。也就是数组中的第一个节点。 
这里写图片描述 
将最后一个节点替代头节点然后进行调整。 。。。。。
 

public E remove() {
        E x = poll();
        if (x != null)
            return x;
        else
            throw new NoSuchElementException();
    }

public E poll() {
        if (size == 0)
            return null;
      //如果堆大小为0则返回null      
        int s = --size;
        modCount++;
        E result = (E) queue[0];
        E x = (E) queue[s];
        queue[s] = null;
//如果堆中只有一个元素直接删除        
        if (s != 0)
            siftDown(0, x);
//否则删除元素后对堆进行调整            
        return result;
    }

private void siftDown(int k, E x) {
        if (comparator != null)
            siftDownUsingComparator(k, x);
        else
            siftDownComparable(k, x);
}

private void siftDownComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>)x;
        int half = size >>> 1;        // loop while a non-leaf
        while (k < half) {
            int child = (k << 1) + 1; // assume left child is least
            Object c = queue[child];
            int right = child + 1;
            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;
            k = child;
        }
        queue[k] = key;
}

如果当前节点比左右节点中的最小节点 小 就 break,否则一直与左右节点的最小节点交换。直到当前节点无子节点。

remove(Object o):

 

public boolean remove(Object o) {
        int i = indexOf(o);
        if (i == -1)
            return false;
        else {
            removeAt(i);
            return true;
        }
}

//O(n)
private 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) {
        // assert i >= 0 && i < size;
        modCount++;
        int s = --size;
        if (s == i) // removed last element
            queue[i] = null;
        else {
            E moved = (E) queue[s];//树末端元素
            queue[s] = null;
            siftDown(i, moved);//从 当前删除位置向下比较
            if (queue[i] == moved) {
                siftUp(i, moved);//从当前位置向上比较
                if (queue[i] != moved)
                    return moved;
            }
        }
        return null;
}

自测用例:

public class A_05_PriorityQueue {
    public static void main(String[] args) {
        KthLargest kthLargest = new KthLargest(3, new int[]{4, 5, 8, 2});
//        System.out.println(kthLargest.add(3));
//        System.out.println(kthLargest.add(5));
//        System.out.println(kthLargest.add(10));
//        System.out.println(kthLargest.add(9));
//        System.out.println(kthLargest.add(4));

        PriorityQueue<Integer> mQueue = new PriorityQueue<>(3);
        mQueue.add(3);
        mQueue.add(2);
        mQueue.add(1);

        System.out.println("remove obj=="+mQueue.remove(Integer.valueOf((2))));
//        customMaxHeap();
    }

    private static void customMaxHeap() {
        //是用 自定义的compare
        PriorityQueue<People> queue = new PriorityQueue<People>(5,
                new Comparator<People>() {
                    public int compare(People p1, People p2) {
//                        return p2.age - p1.age;//Max heap
                        return p1.age - p2.age;//Min heap
                    }
                });

        for (int i = 1; i <= 10; i++) {
//            queue.add(new People("张"+ i, (new Random().nextInt(100))));
            People people = new People("张" + i, (new Random().nextInt(100)));
            queue.add(people);
            System.out.println("add..."+people.toString());
        }
        System.out.println("***********************");
        while (!queue.isEmpty()) {
            System.out.println(queue.poll().toString());
        }
    }
}

class KthLargest {

    //PriorityQueue默认是一个小顶堆,然而可以通过传入自定义的Comparator函数来实现大顶堆
    private final PriorityQueue<Integer> queue;
    private final int k;
    public KthLargest(int k, int[] nums) {
        this.k = k;
        queue = new PriorityQueue<>(k);
        for (int num:nums) {
            add(num);
        }
    }

    public int add(int val) {
        if (queue.size()<k){
            queue.add(val);
        }else if (val>queue.peek()){
            queue.poll();
            queue.offer(val);
        }
        return queue.peek();
    }
}

/**
 * Your KthLargest object will be instantiated and called as such:
 * KthLargest obj = new KthLargest(k, nums);
 * int param_1 = obj.add(val);
 */

class People {
    String name;
    int age;
    public People(String name, int age){
        this.name = name;
        this.age = age;
    }
    public String toString() {
        return "姓名:"+name + " 年龄:" + age;
    }
}

 

參考文章:

https://www.cnblogs.com/gnivor/p/4841191.html

https://blog.youkuaiyun.com/u013309870/article/details/71189189

优先队列PriorityQueue (大根堆/小根堆/TopK问题) PriorityQueue是从JDK1.5开始提供的新的数据结构接口,它是一种基于优先级堆的极大优先级队列。优先级队列是不同于先进先出队列的另一种队列。每次从队列中取出的是具有最高优先权的元素。 默认情况下,PriorityQueue是小顶堆,如代码所示 public static void main(String[] args) { PriorityQueue<Integer> queue = new PriorityQueue<&g... 阅读详情

相关推荐

Java PriorityQueue 深度解析

PriorityQueue是Java集合框架中的一个重要实现类,基于优先级堆(通常是最小堆)实现,能够高效处理按优先级排序的元素。其核心特性包括:基于堆的数据结构、支持自然排序或自定义Comparator、插入和删除操作的时间复杂度为O(log n)、非线程安全、不允许null元素、不保证迭代顺序但保证出队顺序、自动动态扩容。底层实现使用数组表示二叉堆,关键方法如插入(offer)和移除(poll)通过上浮和下沉操作维护堆结构。PriorityQueue支持两种排序方式:自然排序(元素实现Comparabl

记录学习的过程 1026

【Java】PriorityQueue--优先级队列

前面介绍过队列,队列是一种先进先出(FIFO)的数据结构,但有些情况下,操作的数据可能带有优先级,一般出队列时,可能需要优先级高的元素先出队列,该中场景下,使用队列显然不合适,比如:在手机上玩游戏的时候,如果有来电,那么系统应该优先处理打进来的电话. 在这种情况下,数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象。这种数据结构就是优先级队列(Priority Queue)。

weixin_73616913的博客 1万+

ArrayDeque和PriorityQueue解析

文章目录一、ArrayDeque(一)简介(二)主要方法(三)方法实现二、PriorityQueue(一) 简介(二)PriorityQueue 原理(三)方法实现 一、ArrayDeque (一)简介 ArrayDeque类是双端队列的实现类,类的继承结构如下面,继承自AbastractCollection(该类实习了部分集合通用的方法,其实现了Collection接口),其实现的接口Deque...

weixin_44833337的博客 902

优先队列——PriorityQueue详解

优先队列 PriorityQueue(优先队列) 采用的是堆排序, 实际上是一个堆(不指定Comparator时默认为最小堆) 队列既可以根据元素的自然顺序来排序,也可以根据 Comparator来设置排序规则。 队列的头是按指定排序方式的最小元素。如果多个元素都是最小值,则头是其中一个元素。 新建对象的时候可以指定一个初始容量,其容量会自动增加。 注意1: 该队列是用数组实现,但是数组大小可以动态增加,容量无限。 注意2: 队列的实现不是同步的。不是线程安全的。如果多个线程中的任意线程从结构上修改了列表,

新入坑的嵌入式的小白 2万+

Queue常用类解析之PriorityQueue

一、接口介绍 Queue :Collection的子接口,先进先出的集合。 BlockingQueue:Queue的子接口,阻塞式队列,线程安全的队列,主要用于多线程场景尤其是线程池中。 TransferQueue:BlockingQueue的子接口,一个生产者-消费者模式的队列,生产者会阻塞等待着消费者对元素进行消费。 二、PriorityQueue 优先级队列,队列内的元素按照一定的顺序进行排...

翻身已碰头的博客 973

源码解析C#中PriorityQueue(优先级队列)的实现

前言 前段时间看到有大佬对.net 6.0新出的PriorityQueue(优先级队列)数据结构做了解析,但是没有源码分析,所以本着探究源码的心态,看了看并分享出来。它不像普通队列先进先出(FIFO),而是根据优先级出队。 ps:读者多注意代码的注释。 D叉树的认识(d-ary heap) 首先我们在表示一个堆(大顶堆或小顶堆)的时候,实际上是通过一个一维数组来维护一个二叉树(d=2,d表示每个父节点最多有几个子节点),首先看下图的二叉树,数字代表索引: 任意一个节点的父节点的索引为:(inde

学到牛牛官网帐号 1724

PriorityQueue(优先队列)

我们都知道队列是一种先进先出的数据结构,没有优先级,众数据平等. 但是在某些情况下,我们操作的数据可能带有优先级,出队列时要优先级高的先出,低的后出. 在这种情况下,我们的数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象.这种数据结构就是我们今天要介绍的优先级队列.在Java的集成框架中主要有两种类型的优先级队列,分别为:PriorityQueue和PriorityBlockingQueue PriorityQueue是线程不安全的,PriorityBlockingQueue是

Hello World! 8339

WSL2+Ubuntu-22.04搭建PX4仿真环境:从零避坑到一键起飞

本文详细指导在Windows系统上通过WSL2和Ubuntu-22.04搭建PX4仿真开发环境的完整流程,涵盖环境配置、源码下载、依赖安装、Gazebo仿真及常见错误解决方案,帮助开发者从零避坑实现无人机一键起飞仿真。

k8s6orchestrator的博客 670

PriorityQueue的深入解析

这里写自定义目录标题PriorityQueue的深入解析一:体系结构二:PriorityQueue的概念三:使用分析自然排序:输出结果:自定义排序:输出结果:四:源码分析4.1 以此示例代码为引子分析PriorityQueue源码4.2 第一段代码分析:4.3 第二段代码分析:4.4 第三段代码分析:5:总结 PriorityQueue的深入解析 一:体系结构 二:PriorityQueue的概...

qq_42031826的博客 886

深入解析经典蓝牙连接:从HCI命令到LMP协议的完整流程

本文深入解析经典蓝牙连接的完整流程,从设备发现、寻呼建立物理链路,到安全配对与加密的每一步。通过对照HCI LOG与AIR LOG,详细拆解了HCI命令与LMP协议报文的对应关系,帮助开发者精准定位连接问题,掌握蓝牙调试的核心方法。

weixin_29260291的博客 449

TensorFlow实战:5步搞定因果推断模型TARNet(附完整代码)

本文提供了一份基于TensorFlow的TARNet因果推断模型实战指南。通过五个清晰步骤,从数据预处理、模型构建到训练评估,详细讲解了如何利用深度模型解决营销评估、疗效分析等场景中的因果效应估计问题,并附有完整代码解决样本不平衡等工程挑战。

python9snake的博客 415

Collection - PriorityQueue源码解析

前面以Java ArrayDeque为例讲解了Stack和Queue,其实还有一种特殊的队列叫做PriorityQueue,即优先队列。优先队列的作用是能保证每次取出的元素都是队列中权值最小的(Java的优先队列每次取最小元素,C++的优先队列每次取最大元素)。这里牵涉到了大小关系,元素大小的评判可以通过元素本身的自然顺序(natural ordering),也可以通过构造时传入的比较器(Comparator,类似于C++的仿函数)。

珍惜当下 629

计算机组成原理(第三版)唐朔飞-第六章计算机的运算方法-课后习题(17-32)

计算机组成原理(第三版)唐朔飞-第六章计算机的运算方法-课后习题(17-32)

weixin_45735391的博客 12万+

PriorityQueue源码解析

PriorityQueue为优先队列,其与普通的FIFO(先进先出)队列不同的地方是,优先队列每次出队的均是队列中优先级最高的元素。 PriorityQueue使用最小(大)二叉堆来管理队列中的元素,元素的大小比较方法由用户指定的Comparator指定,每次出队元素为堆顶元素。 最小二叉堆 最小二叉堆满足如下约束: 完全二叉树; 所有非叶子节点的根节点value均小于其左、右子节点的value。 如下图即为典型的最小二叉堆: 入队 最小二叉堆的元素添加过程如下: 将待添加元素添加到二叉树的最后一个

打酱油的葫芦娃 5725

论文阅读-Whisper语音识别(OpenAI)

Whisper 是一个自动语音识别(ASR,Automatic Speech Recognition)系统,OpenAI 通过从网络上收集了 68 万小时的多语言,其中11万小时覆盖96 种语言,12万小时的语言标号为英语,用多任务(multitask)监督数据对 Whisper 进行了训练。

一位不是很硕的鉴伪方向硕士 6366

基于STM32F1XX 、HAL库的OPT4001-Q1环境光传感器驱动应用C语言程序设计

OPT4001-Q1 的输出是半对数 的,具有 9 个二进制对数满量程照度范围,每个范围内具有高度线性响应,使得 PicoStar™ 型号的测量能力为 312.5μlux 至 84klux,USON 型号的测量能力为 400µlux 至 107klux。// 禁用中断(示例)

PP_Cottage的博客 461

PriorityQueue 源码解析

简介 PriorityQueue是基于小顶堆实现的优先队列,它的头部总是指向当前队列中最小的元素。关于小顶堆的知识可以参考严蔚敏的《数据结构》。 // 初始的数组长度 private static final int DEFAULT_INITIAL_CAPACITY = 11; /** * 优先队列实质就是小顶堆。 * 下标为i的节点,其两个孩子...

追梦程序员的博客 247

Siemens SIMATIC软件二次开发:使用C#进行SIMATIC软件二次开发

通过使用OPC-UA通信协议和S7通信库,可以有效地在C#应用程序中与Siemens SIMATIC软件进行数据交换。OPC-UA提供了一个标准化的平台,而S7通信库则提供了直接访问PLC数据的能力。掌握这两种通信方式,将极大地增强在工业自动化领域的二次开发能力。在Siemens SIMATIC软件中,自定义功能块开发是实现二次开发的关键步骤之一。通过C#,可以创建高度定制化的功能块,以满足特定的控制需求。下面,我们将通过一个示例来展示如何使用C#开发一个自定义功能块。OPC-UA通信。

kkchenjj的博客 1414

PriorityQueue深入解析

PriorityQueue介绍     在平时的编程工作中似乎很少碰到PriorityQueue(优先队列) ,故很多人一开始看到优先队列的时候还会有点迷惑。优先队列本质上就是一个最小堆。前面一篇文章介绍了堆排序和堆的性质。而堆又是什么呢?它是一个数组,不过满足一个特殊的性质。我们以一种完全二叉树的视角去看这个数组,并用二叉树的上下级关系来映射到数组上面。如果是最大堆,则二叉树的顶点是保存的最大

kobejayandy的专栏 8286

AMDGPU驱动中Doorbell与Event机制简要对比

摘要:AMDGpu驱动中,Doorbell和Event是两种异步通知机制。Doorbell是CPU→GPU的单向硬件通知,通过写MMIO寄存器触发GPU拉取任务,延迟低且无需内核介入,适用于队列任务提交。Event是GPU→CPU的双向同步通知,基于中断和内核事件对象,支持阻塞等待,用于任务完成或异常上报。核心区别在于方向、用途和实现方式:Doorbell侧重任务触发,Event侧重状态反馈。两者配合实现高效的任务下发与完成通知流程。

图形/计算GPU的驱动与虚拟化技术分享园地 1850

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

java queue-PriorityQueue源码解析

一、概念1. 它是基于优先级堆的无界优先级队列;2. 其元素是按照自然排序或者在队列构建时提供的方法来进行排序;3. 队列中不允许存在null元素;4. 它不是多线程安全的; 5. 它使用的是二叉堆,二叉堆分为大根堆和小根堆,大根堆是父节点的值总是大于或等于子节点的值,小根堆是父节点的值总是小于或等于子节点的值;6. 它总是一棵完全而查实或近似完全二叉树;7. 它内部的实现是基于数组格式的,对于数...

qq_41786692的博客 755

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Java数据结构之PriorityQueue全面解析

本文深入探讨了Java中的优先队列(PriorityQueue)实现。优先队列是一种按元素优先级排序的特殊队列,采用堆数据结构实现,打破普通队列的FIFO规则。文章详细分析了堆的基本概念和核心操作(上浮、下沉),并手动实现了一个优先队列,包含大根堆/小根堆的构建、插入、删除等操作。同时介绍了Java自带PriorityQueue的使用方法,包括默认小根堆和通过比较器实现大根堆,并以TopK问题为例展示了实际应用。通过底层原理剖析和代码实现,帮助读者全面掌握这一重要数据结构。

zxk1314的博客 1412

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

上一篇: 批处理 bat for 详解
下一篇: Stack Queue Deque
阿杰、
博客等级 码龄11年 13粉丝 47原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值