优先级队列(堆)(2)

news2024/11/22 22:32:48

目录

一. PriorityQueue的特性

二.  PriorityQueue常用接口介绍

1. 优先级队列的构造

2. 转成大根堆存储方法:

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

三. Top-k问题


一. PriorityQueue的特性

Java 集合框架中提供了 PriorityQueue PriorityBlockingQueue 两种类型的优先级队列, PriorityQueue 是线 程不安全的, PriorityBlockingQueue 是线程安全的 ,本文主要介绍 PriorityQueue
关于 PriorityQueue 的使用要注意:
1. 使用时必须导入 PriorityQueue 所在的包,即:
import java . util . PriorityQueue ;
2. PriorityQueue 中放置的 元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出
ClassCastException 异常
Student类目前无法比较, 不能插入
3. 不能 插入 null 对象,否则会抛出 NullPointerException
4. 没有容量限制,可以插入任意多个元素,其内部可以自动扩容
5. 插入和删除元素的时间复杂度为O(\log_{2}n)
6. PriorityQueue 底层使用了堆数据结构
7. PriorityQueue 默认情况下是小堆 --- 即每次获取到的元素都是最小的元素

二.  PriorityQueue常用接口介绍

1. 优先级队列的构造

PriorityQueue 中常见的几种构造方式:
// 创建一个空的优先级队列,底层默认容量是 11
PriorityQueue < Integer > q1 = new PriorityQueue <> ();
// 创建一个空的优先级队列,底层的容量为 initialCapacity
PriorityQueue < Integer > q2 = new PriorityQueue <> ( 100 );
// ArrayList 对象来构造一个优先级队列的对象
ArrayList < Integer > list = new ArrayList <> ();
list . add ( 4 );
list . add ( 3 );
list . add ( 2 );
list . add ( 1 );
PriorityQueue < Integer > q3 = new PriorityQueue <> ( list );

注意:默认情况下,PriorityQueue队列是小根堆,如果需要其他比较方法需要用户提供比较器 

例如上面的学生类, 如果想要插入优先级对列, 必须继承Comparator接口, 并重写CompareTo方法, 提供比较方法, 就可以传入优先级队列

class Student implements Comparable<Student>{
    public String name;
    public int age;

    public Student(String name,int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public int compareTo(Student o) {
        return this.age-o.age;
    }
}
public class Test {
    public static void main(String[] args) {
        Student student1 = new Student("zhangsan",12);
        Student student2 = new Student("lisi",14);
        Student student3 = new Student("wangwu",6);
        Student student4 = new Student("zhaoliu",4);
        PriorityQueue<Student> priorityQueue = new PriorityQueue<>();
        priorityQueue.offer(student1);
        priorityQueue.offer(student2);
        priorityQueue.offer(student3);
        priorityQueue.offer(student4);
        System.out.println(student1.compareTo(student2));

    }
}

可以看到是小根堆存储的

第四种构造方法:

我们点进去PriorityQueue的源码就会发现, 构造方法还可以传比较器

我们可以写一个比较器, 类可以不用继承Comparator接口了, 接着通过传比较器就可以进行比较了, 就可以插入优先级队列

class Student {
    public String name;
    public int age;

    public Student(String name,int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
class NameComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1,Student o2) {
        return o1.name.compareTo(o2.name);
    }
}
public class Test {
    public static void main(String[] args) {
        Student student1 = new Student("zhangsan",12);
        Student student2 = new Student("lisi",14);
        Student student3 = new Student("wangwu",6);
        Student student4 = new Student("zhaoliu",4);
        NameComparator nameComparator = new NameComparator();
        PriorityQueue<Student> priorityQueue = new PriorityQueue<>(nameComparator);
        priorityQueue.offer(student1);
        priorityQueue.offer(student2);
        priorityQueue.offer(student3);
        priorityQueue.offer(student4);
        System.out.println(priorityQueue.peek());//验证:如果是通过name进行排序, 那么堆顶元素应该是lisi
        System.out.println(nameComparator.compare(student1,student2));

    }

 那么, 如果我们想要大根堆该怎么实现呢?

2. 转成大根堆存储方法:

转成大根堆的第一种方式:重写comepareTo接口

先来看一下源码中小根堆是怎么实现的:

还是学生的类举例:

此时是小根堆存储

offer():

siftUp():

因为我们没有传入比较器, 那么就进入else中, 进入siftUpComrarable()中

siftUpComrarable():

我们可以看到, 使用的是compareTo方法, key表示新传入数字的值, e表示此节点的父亲节点的值key.compareTo(e),  compareTo是通过key - e来计算的, 如果key.compareTo(e) >= 0 , 说明key>e, 就会break, 不交换两个数, 此时的存储顺序是e -> key, 那么存储的方式就是小根堆

如果我们重写compareTo方法, 通过e - key进行计算, 那么如果key.compareTo(e) >= 0 , 说明key<e, 就会break, 不交换两个数, 此时的存储顺序还是e -> key, 那么存储的方式就是大根堆

那么, 我们将上面绿的框的代码进行更改

变成大根堆存储了!

转成大根堆的第二种方式:传入比较器

 先写一个简单的代码:

 offer():

siftUp():

假设我们传入了比较器, 就会进入siftUsingComparator方法中
siftUsingComparator():

可以看到调用的是比较器中的compare方法, 所以我们可以在比较器中更改比较的方法, 来实现大根堆还是小根堆存储

我们可以写一个比较器:

此时是小根堆存储

此时是大根堆存储

 

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

优先级队列的扩容说明:
如果容量小于 64 时,是按照 oldCapacity 2 倍方式扩容的
如果容量大于等于 64 ,是按照 oldCapacity 1.5 倍方式扩容的
如果容量超过 MAX_ARRAY_SIZE ,按照 MAX_ARRAY_SIZE 来进行扩容

 

三. Top-k问题

TOP-K 问题:即求数据集合中前 K 个最大的元素或者最小的元素,一般情况下数据量都比较大
比如:专业前 10 名、世界 500 强、富豪榜、游戏中前 100 的活跃玩家等。
oj链接 链接

 

思路:

找最大最小的问题, 我们首先想到使用堆, 因为如果是大根堆, 那么堆顶元素就是最大值, 如果是小根堆, 那么堆顶元素就是小根堆

思路1:我们可以将所有的数据放入堆中, 想到得到前k小的数据, 就循环弹出k个元素即可, 但上述思路的时间复杂度很高

思路2:为了尽可能减小时间复杂度, 我们可以建立只有k个结点的堆,可以取数组的前k个数, 但是找前k小的元素, 就要建立大根堆, 这样堆顶元素就是k个元素中最大的, 我们从数组下标为k的位置开始遍历, 用数组后面的数据和堆顶元素进行比较, 如果有比堆顶元素小的元素, 那么就删除堆顶元素, 将这个数放进来, 此时堆顶元素就是此时堆中最大的元素, 循环比较下去, 直到遍历完成数组, 此时数组中存放的就是最小的k个数, 存放在返回数组中即可
class Imp implements Comparator<Integer>{
    public int compare(Integer o1,Integer o2){
        return o2 - o1;
    }
}
class Solution {
   public static int[] smallestK(int[] arr, int k) {
        Imp imp = new Imp();
        int[] tmp = new int[k];
        if(k == 0){
            return tmp;
        }
        PriorityQueue<Integer> maxHeap = new PriorityQueue<>(imp);
        for (int i = 0; i < k; i++) {
            maxHeap.offer(arr[i]);
        }
        int i = k;
        while(i < arr.length){
            if(maxHeap.peek() > arr[i]){
                maxHeap.poll();
                maxHeap.offer(arr[i]);
            }
            i++;
        }
        for (int j = 0; j < k; j++) {
            tmp[j] = maxHeap.poll();
        }
        return tmp;
    }
}

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

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

相关文章

PowerShell 无法保留窗口和字体设置

那么&#xff0c;首先&#xff0c;得亮一下版本&#xff0c;默认软件&#xff1a; PS C:\Windows> $PSVersionTableName Value ---- ----- PSVersion 5.1.19041.4170 PSEdition …

Win11初始化系统遇一文解决

这个是目录 一、设置内的初始化无法使用时&#xff0c;使用以下工具二、将桌面移动到D盘三、解决win11桌面右键创建只有一个带盾牌的文件夹问题四、win11 系统停止更新五、office安装1、使用的是 Office Tool plus2、使用WPS 六、D盘有感叹号七、打开组策略编辑器(gpedit.msc)失…

安卓转鸿蒙能有多适配?简直了……

到现在为止&#xff0c;想必很多开发者都或多或少 了解过鸿蒙。许多企业也都已经加入了鸿蒙业务&#xff0c;半推半就的开始学习鸿蒙开发。那么鸿蒙到底好不好搞呢&#xff1f; 首先可以肯定的一点&#xff0c;对于做安卓的来说鸿蒙非常搞&#xff0c;究竟有多好搞呢&#xff…

SQL语句之SELECT语句

一般格式 SELECT DISTINCT/ALL 目标列表达式 //要显示的属性列 FROM 表名/视图名 //查询的对象 WHERE 条件表达式 //查询条件 GROUP BY 列名 HAVING 条件表达式 //查询结果分组 ORDER BY 列名 次序; //最终查询结果排序 文章目录 一、基本查询 1、SELECT 目标列表达…

系统设计实例(一)百万级别用户系统

二、百万级别用户系统 原则&#xff1a; 尽可能地缓存数据采用无状态Web层支持多个数据中心在 CDN 中托管静态资源通过分片扩展数据层将层级拆分为独立的服务 负载均衡器 负载均衡器会将传入的流量均匀分配给在负载均衡集合中定义的Web服务器&#xff0c;用户直接连接负载均…

数据结构与算法3-选择排序

文章目录 1. 认识选择排序2. 图示2.1 图示12.2 图示2 3. 代码 1. 认识选择排序 双层for循环&#xff0c;每次选出最小的数放到i位置&#xff0c;时间复杂度O( n 2 n^2 n2)&#xff0c;空间复杂度O(1);从未排序的序列中找到最小&#xff08;或最大&#xff09;的元素&#xff0…

Windows server Database 2025 安装 i225/i226 网卡驱动

windows这比坏得很&#xff0c;intel消费级网卡不准在服务器系统上安装。你要说他是异构不支持&#xff1f;他就纯粹恶心人。 之前已经安装过一次&#xff0c;但是今天database预览版一更新&#xff0c;又给我把网卡驱动杀了&#xff0c;气死&#xff0c;写一篇教程。 1.去官网…

cordova安装安卓版本,遇到的各种坑。折腾了两天才弄好

cordova官网地址 https://cordova.apache.org/docs/en/12.x/guide/cli/index.html 1. 输入命令 npm install -g cordova 全局安装cordova 2. 创建文件和项目以及app的应用名称 cordova create hello com.example.hello HelloWorld 我写的是这个 cordova create myApp 3.co…

基于Springboot的员工健康管理系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的员工健康管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构…

【Linux】盘点广义层面上【三种最基本的进程状态】

前言 大家好吖&#xff0c;欢迎来到 YY 滴 Linux系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过Linux的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的…

cmd中cd命令无法进入文件目录

问题&#xff1a;在cmd中用cd命令进入不了目录 解决办法&#xff1a;在cd后面加/d&#xff08;cd和/d之间有空格&#xff09; windows的cmd命令cd和cd /d的区别&#xff1a; 1. cd 命令: - cd 是 "change directory" 的缩写。 - 默认情况下&#xff0c;cd 命令…

DNA存储技术原理是什么?

随着大数据和人工智能的发展&#xff0c;全球每天产生的数据量剧增&#xff0c;对存储设备的需求也随之增长&#xff0c;数据存储问题日益凸显。传统的硬盘驱动器&#xff08;HDD&#xff09;、磁带等冷存和深度归档存储占据数据中心存储的60-70%&#xff0c;由于它们的访问频率…

Java特性之设计模式【组合模式】

一、组合模式 概述 组合模式&#xff08;Composite Pattern&#xff09;&#xff0c;又叫部分整体模式&#xff0c;是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象&#xff0c;用来表示部分以及整体层次。这种类型的设计模式属于结构型模式&#x…

【YUNBEE云贝-PostgreSQL】FDW应用

注: 本文为云贝教育 刘峰 原创&#xff0c;请尊重知识产权&#xff0c;转发请注明出处&#xff0c;不接受任何抄袭、演绎和未经注明出处的转载。 前言 Wrapper&#xff08;FDW&#xff09;是一项关键特性&#xff0c;它赋予数据库用户直接通过SQL语句访问存储于外部数据源的能…

Jumpserver 堡垒机用户启用双因子登录

前言&#xff1a; 堡垒机双因子登录 堡垒机往往是内部权限的集合体&#xff0c;拿到了堡垒机的用户账号密码&#xff0c;很容易就顺藤摸瓜攻破各种应用系统&#xff0c;除了常规的用户名复杂密码的要求外&#xff0c;我们常常都要求采用双因子的登录方式。双因子最常见的就是账…

【Super数据结构】先进先出/后进先出,队列和栈代码实现+应用场景

&#x1f3e0;关于此专栏&#xff1a;Super数据结构专栏将使用C/C语言介绍顺序表、链表、栈、队列等数据结构&#xff0c;每篇博文会使用尽可能多的代码片段图片的方式。 &#x1f6aa;归属专栏&#xff1a;Super数据结构 &#x1f3af;每日努力一点点&#xff0c;技术累计看得…

HarmonyOS如何使用低代码实现界面布局

介绍 本篇Codelab是基于ArkTS语言的低代码开发方式实现的一个简单实例。具体实现功能如下&#xff1a; 创建一个低代码工程。通过拖拽的方式实现任务列表和任务信息界面的界面布局。在UI编辑界面实现数据动态渲染和事件的绑定。 最终实现效果如下&#xff1a; 相关概念 低代…

ECharts5 概念篇2

数据转换 数据转换基础使用 在 echarts 中&#xff0c;数据转换是依托于数据集&#xff08;dataset&#xff09;来实现的. 我们可以设置 dataset.transform 来表示&#xff0c;此 dataset 的数据&#xff0c;来自于此 transform 的结果。下面是上述例子的效果&#xff0c;三个饼…

冶炼金属(二分)

题目描述&#xff1a; 小蓝有一个神奇的炉子用于将普通金属 O 冶炼成为一种特殊金属 X。 这个炉子有一个称作转换率的属性 V&#xff0c;V是一个正整数&#xff0c;这意味着消耗 V个普通金属 O 恰好可以冶炼出一个特殊金属 X&#xff0c;当普通金属 O 的数目不足 V 时&#x…

【机器学习】基于粒子群算法优化的BP神经网络分类预测(PSO-BP)

目录 1.原理与思路2.设计与实现3.结果预测4.代码获取 1.原理与思路 【智能算法应用】智能算法优化BP神经网络思路【智能算法】粒子群算法&#xff08;PSO&#xff09;原理及实现 2.设计与实现 数据集&#xff1a; 多输入多输出&#xff1a;样本特征24&#xff0c;标签类别4…