【数据结构】优先级队列(堆)与PriorityQueue

news2024/11/18 13:43:36

目录

一、堆

二、Java里的集合类PriorityQueue

1、优先级队列的概念

2、构造方法

3、常用方法

1.入队offer

2.出队poll

3.获取队首元素peek

4.扩容机制

4、 注意事项

三、实现大根堆

1、准备字段

2、创建大根堆

3、offer

4、poll

5、peek


一、堆

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

二、Java里的集合类PriorityQueue

1、优先级队列的概念

在出队的时候返回的是优先级最高的元素,这种队列叫做优先级队列

2、构造方法

构造方法说明
PriorityQueue()无参构造,此时默认长度为11
PriorityQueue(int initialCapacity)传入队列的长度
PriorityQueue(Comparator<? super E> comparator)传入比较器,用于后续优先级比较
PriorityQueue(nt initialCapacity,Comparator<? super E> comparator)同时传入长度与比较器
PriorityQueue(Collection<? extends E> c)
通过其他集合类构造优先级队列

3、常用方法

1.入队offer

如果传入的对象为空就抛出异常。第一次插入直接放在下标为0的地方,如果后续存入,就调用向上调整进而维护为堆,向上调整在下面实现堆中会讲到

2.出队poll

出队操作本质是将队首也就是下标为0的元素与队尾元素进行交换,然后让有效数据个数size--,再调用向上调整维护堆结构

 

3.获取队首元素peek

获取下标为0的元素

4.扩容机制

如果在堆满的情况下入队,就会调用grow进行扩容,而原码实现扩容时,如果现在容量小于64则采取2倍扩容,超出后则1.5倍扩容

 

4、 注意事项

使用PriorityQueue时传入的类型要实现Comparable接口或者在构造方法时手动传入比较器,且传入空会异常,如果在多线程情况下推荐使用PriorityBlockingQueue

三、实现大根堆

1、准备字段

public class MyHeap <T extends Comparable<T>{
    private Object[] queue;   //底层数组
    private int size;    //有效数据个数
    
    private static final int DEFAULT_SIZE = 11;

    /**
     * 构造方法默认长度为11
     */
    public MyHeap(){
        this.queue = new Object[DEFAULT_SIZE];
    }

    /**
     * 构造方法初始数组长度
     * @param initialCapacity 容量
     */
    public MyHeap(int initialCapacity){
        this.queue = new Object[initialCapacity];
    }
}

2、创建大根堆

在使用构造方法传入一个数组时,这个数组不是堆,我们调用heapIfy方法将其先创建成一个大根堆

/**
     * 传入数组
     * @param e
     */
    public MyHeap(Object[] e){
        this.queue = e;
        this.size = e.length;
        heapIfy();
    }

    /**
     * 创建大根堆
     */
    private void heapIfy(){
        for(int parent = (size - 1 - 1) / 2; parent >= 0; parent--){
            siftDown(parent,this.size);
        }
    }

比如一组数据 1 2 3 4 5 6,我们将他创建成一个大根堆时,我们先从最后一个位置所在的树开始进行调整,由于我们能获取到数组的长度,且根据堆的性质父亲节点的下标 * 2 + 1就孩子节点下标可知,知道孩子的下标(数组长度-1)就能知道父亲的下标。拿到3后与孩子里的最大值进行比较,如果孩子大就交换

 

 

交换之后,如果孩子下面还有数据

 

此时即需要继续进行调整,此时让原来的父亲变为原来的孩子,让原来的孩子变为现在父亲的孩子 

继续重复上述的操作,比较交换,直到孩子的下标不存在时或者不再需要交换时即可

/**
     * 传入数组
     * @param e
     */
    public MyHeap(Object[] e){
        this.queue = e;
        this.size = e.length;
        heapIfy();
    }

    /**
     * 创建大根堆
     */
    private void heapIfy(){
        for(int parent = (size - 1 - 1) / 2; parent >= 0; parent--){
            siftDown(parent,this.size);
        }
    }

    /**
     * 向下调整
     * @param parent
     * @param size
     */
    private void siftDown(int parent, int size) {
        int child = parent * 2 + 1;
        while(child < size){
            //先找到两个孩子的最大值,要保证有右孩子也就是child+1得存在
            if(child + 1 < size && ((T)queue[child]).compareTo((T)queue[child + 1]) <= 0){
                //此时右孩子大,下标右移
                child++;
            }

            //再将最大值与父亲比较
            if(((T)queue[child]).compareTo((T)queue[parent]) <= 0){
                //此时父亲节点大直接结束循环
                break;
            }else{
                //交换两个值
                T tmp = (T)queue[child];
                queue[child] = (T)queue[parent];
                queue[parent] = tmp;

                //再向下走
                parent = child;
                child = 2 * parent + 1;
            }
        }
    }

3、offer

我们在实现入队时,先让数据存入数组的最后一个位置,然后再进行向上调整将数据维护成大根堆

比如我们在大根堆 4 3 2 2 1 1的堆里插入11,先将11存在最后,然后拿他与父亲比较,如果大就交换,如果不比父亲大那他就是大根堆不需要调整,此时交换后,要继续更换父亲与儿子的位置重复比较交换操作,直到孩子下标小于或者等于0时不在需要调整

 

/**
     * offer
     * @param data
     */
    public void offer(T data){
        //1.如果满了就扩容
        if(size == queue.length){
            grow();
        }

        //2.存入数组末尾
        queue[size++] = data;

        //3.进行向上调整
        siftUp(size - 1);
    }

    /**
     * 扩容
     */
    private void grow() {
        queue = Arrays.copyOf(queue,queue.length * 2);
    }

    /**
     * 向上调整
     * @param child
     */
    private void siftUp(int child) {
        int parent = (child - 1) / 2;
        while(child > 0){
            //找到父亲的下标,与父亲进行比较
            if(((T)queue[child]).compareTo((T)queue[parent]) <= 0){
                //父亲的值大直接结束
                break;
            }else{
                //进行交换
                T tmp = (T)queue[child];
                queue[child] = (T)queue[parent];
                queue[parent] = tmp;

                //跟新父亲与儿子的位置
                child = parent;
                parent = (child - 1) / 2;
            }
        }
    }

4、poll

出队操作是将队首元素返回并删除,我们实现时,先记录他的值,然后在将他与最后一个元素交换位置,将有效数据个数-1(删除),此时只有0下标需要调整,然后此时再将数组进行向下调整即可维护大根堆

/**
     * poll
     * @return
     */
    public T poll(){
        //1.如果空就返回null
        if(isEmpty()){
            return null;
        }

        //2.记录值然后交换删除
        T tmp = (T)queue[0];
        queue[0] = (T)queue[size - 1];
        queue[size - 1] = tmp;
        size--;                  //delete

        //3.最后调整
        siftDown(0,size);

        //4.返回队首元素
        return tmp;
    }

    /**
     * 判空
     * @return
     */
    private boolean isEmpty() {
        return size == 0;
    }

5、peek

获取队首元素,也就是0下标的元素

 /**
     * peek
     * @return
     */
    public T peek(){
        //1.判空
        if(isEmpty()){
            return null;
        }

        //2.返回
        return (T)queue[0];
    }

.

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

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

相关文章

Selenium WebDriver定位策略(一)

WebDriver中使用的定位策略列表: 下面以百度搜索输入框为例进行讲解 1、按ID定位策略 通过元素的id属性来定位&#xff0c;前提&#xff1a;元素必须有id属性 driver.find_element_by_id("kw")2、按名称查找策略 通过元素的name属性来定位&#xff0c;前提&…

Go语言设计与实现 -- 关键字for和range

如果我们查看汇编代码的话&#xff0c;可以发现&#xff0c;经过优化的for-range循环的汇编代码和普通for的结构相同。也就是说&#xff0c;使用for-range的控制结构最终也会被Go语言编译器换成普通的for循环。 现象提出 现象1&#xff1a;循环永动机 func main() {arr : []…

如何在anaconda中配置graphviz包

文章目录GraphViz简介一&#xff1a;安装graphviz二&#xff1a;配置环境变量三&#xff1a;检测Graphviz是否配置成功。四&#xff1a;安装graphviz包GraphViz简介 graphviz是贝尔实验室开发的一个开源的工具包&#xff0c;它使用一个特定的DSL(领域特定语言):dot作为脚本语言…

android apk 目录结构

APK的目录结构 更改APK的后缀后&#xff0c;可以看到APK的组成如下&#xff1a; assets 其中assets中存放静态资源。 Res res中存放静态资源。 与assets不同之处&#xff0c;res文件夹下的所有文件会生成资源Id.lib 包括依赖的jar包库&#xff0c;so文件等。 so文件是利…

GD32F450以太网(2-2): PHY芯片IP101GR介绍

PHY芯片IP101GR 文章目录PHY芯片IP101GR1. 预备知识2. IP101GR简介3. IP101GR基于RMII接口的PCB设计重点解析3.1 时钟设置3.2. led灯设计3.3. PHY芯片地址设置4. pcb设计5. 寄存器描述6. 附加&#xff1a;IP101GR和GD32F450引脚连接情况1. 预备知识 接上文 《GD32F450以太网(…

液晶OLED接口MIPI之DSI协议学习

文章目录一、概念介绍MIPI----MIPI联盟发起的为移动应用处理器制定的开放标准MIPI-DSI---Display Serial Interface 2定义了处理器和显示模组之间的高速串行接口DCS---Display Command Set 显示命令集合&#xff08;MIPI-DSI的command模式使用通用标准命令&#xff09;DSC---Di…

字符串函数剖析(3)---strstr函数

1.strstr函数的巧妙 – 查找子字符串 1.1模拟实现strstr函数 strstr函数&#xff1a;在一个字符串中查找子串 学习新函数时&#xff0c;先去c库查找该函数的相关资料&#xff0c;更加助于你的学习 const char * strstr ( const char * str1, const char * str2 );先看函数的…

测开工具:spring boot 实现同步数据库表结构(持续更新)

一、使用场景 一个项目&#xff0c;有多套开发环境。有一套标准的数据库&#xff0c;不同的开发环境&#xff0c;有各自的一套数据库。 标准数据库的表结构经常发生变化&#xff0c;不同的开发环境中的数据库&#xff0c;需要与标准数据库的表结构保持一致。当标准数据库表结…

HNU编译原理实验一cminus_compiler-2022-fall

前言&#xff1a;实验不是很难&#xff0c;主要考察正则表达式部分 lab1实验报告实验要求 根据cminux-f的词法补全lexical_analyer.l文件&#xff0c;完成词法分析器&#xff0c;能够输出识别出的token&#xff0c;type ,line(刚出现的行数)&#xff0c;pos_start(该行开始位置…

[机缘参悟-95] :不同人生、社会问题的本质

事情的本质是物极必反&#xff08;轮回、周期&#xff09; 社会的本质是优胜劣汰&#xff08;迭代、发展&#xff09; 道德的本质是伦理秩序&#xff08;未定、秩序&#xff09; 战争的本质是资源占用&#xff08;弱肉、强食&#xff09; 商业的本质是价值交换 金钱的本质…

基于JAVA SpringBoot+ JWT+Redis的ERP系统,VUE+Element-UI 前后端分离的Saas平台

项目简介 简云Saas平台 基于SpringBoot2.2.0, Mybatis, JWT, Redis, VUEElement-UI 的前后端分离的Saas平台管理系统 在线报表开发 在线表单设计 工作流设计 自定义打印模板定义 **产品分二个版本: 开源版本(包含了系统基础架构在线表单设计). 此版本代码完全开源ERP版本…

Flink-基本的合流操作

文章目录1.基本的合流操作2.1联合&#xff08;Union&#xff09;2.2 连接&#xff08;Connect&#xff09;2.基于时间的合流——双流联结&#xff08;Join&#xff09;2.1 窗口联结&#xff08;Window Join&#xff09;2.2 间隔联结&#xff08;Interval Join&#xff09;2.3 窗…

《面向对象分析与设计》总结

面向对象的软件工程1 面向对象的演化1.1 生活中复杂系统的特点1.2 软件系统的复杂性1.2.1 复杂性的四个方面1.2.1.1 问题域的复杂性1.2.1.2 管理开发的困难性1.2.1.3 软件中的灵活性1.2.1.4 描述离散系统行为1.2.2 复杂系统的五个属性1.2.2.1 层次结构1.2.2.1.1 对象结构1.2.2.…

数据分析神器:数据自动录入并生成BI报表

做报表、分析数据、做汇报是许多打工人的日常&#xff0c;每天都要耗费不少的时间用Excel来整理、清洗数据和生成好看的报表。如果这些数据都是手动整理、复制粘贴的话&#xff0c;不仅费时费力&#xff0c;而且很容易出错。 在越来越多企业采用SaaS产品和不同数据应用的今天&…

来看一个vue-element-表单之登录页面,最后送上一个登录页面

vue-element 表单之登录页面使用 0. 先留下属性表格 表单验证&#xff1a;Form 组件提供了表单验证的功能&#xff0c;只需要通过 rules 属性传入约定的验证规则&#xff0c;并将 Form-Item 的 prop 属性设置为需校验的字段名即可。 1. 表单属性表(el-form) 2. 表单项属性表…

php工作流引擎再发新版本—Tpflow7.0重磅发布

2022年已接近尾声&#xff0c;又到了每年发布大版本的时候&#xff0c;Tpflow历经一个多月的意见征集及版本优化&#xff0c;从底层改进&#xff0c;从UI调整&#xff0c;增强了事件功能。 发布日期&#xff1a;2022年12月23日 发布版号&#xff1a;V7.0.0 Tpflow 工作流引擎…

短视频引流+私域流量沉淀,一个全新的短视频和链动模式结合方案

在微盟企微助手微盟智慧零售团队的协助下&#xff0c;今年7月底么么茶正式开始运营企微私域&#xff0c;截至当前&#xff0c;在短短3个月时间已成功沉淀7万私域客户&#xff0c;线上商城GMV超145万。 么么茶旅拍的核心流量来源自公域短视频平台&#xff0c;品牌基于服务覆盖下…

OB0206 obsidian 表格编辑插件:advanced Tables插件使用

序号解读&#xff1a; 01——软件基础使用、基础语法 02——插件使用 03——综合实战 0 写在前面 Ob社区插件汇总&#xff1a;Airtable - OB社区插件汇总 - Johnny整理 - 每周更新 - B站 Johnny学Explore the "OB社区插件汇总 - Johnny整理 - 每周更新 - B站 Johnny学&qu…

执行操作后的变量值,我的题解首次优于官方

2011. 执行操作后的变量值 难度简单46 存在一种仅支持 4 种操作和 1 个变量 X 的编程语言&#xff1a; X 和 X 使变量 X 的值 加 1--X 和 X-- 使变量 X 的值 减 1 最初&#xff0c;X 的值是 0 给你一个字符串数组 operations &#xff0c;这是由操作组成的一个列表&#xf…

求N阶矩阵的幂(一维,二维多种方法)

引用&#xff1a;对于矩阵的计算想必都是一件很头疼的事情吧&#xff0c;因为计算量是比较大&#xff0c;因为你要用前一个矩阵的行乘以后矩阵的列且对应相加才得到新矩阵的第一个元素&#xff0c;且两个矩阵可以相乘的条件也是前一个矩阵的列等于后一个矩阵的行&#xff0c;操…