优先级队列(概念理解/底层模拟/时间复杂度分析)

目录

1.概念理解

2.优先级队列的底层模拟

2.1堆的概念  

2.2优先队列的模拟实现

2.2.1把Heap类定义好

2.2.2初始化堆

2.2.3创建大堆

1.思路

以此二叉树为例:

图文理解:

2.思路转化为代码

2.2.4堆操作之offer(进队列)

1.思路

2.思路转化为代码:

2.2.5堆操作之poll(删除)

思路:

图片演示:

思路转化为代码:

2.2.6 isFuLL和isEmpty

2.3建堆的时间复杂度


1.概念理解

在之前的数据结构学习中,我们都知道队列是一个先进先出(FIFO)的数据结构,但有些情况下,操作的数据可能带有优先级,一般出队 列时,可能需要优先级高的元素先出队列,该中场景下,使用队列显然不合适,比如:在手机上玩游戏的时候,如果有来电,那么系统应该优先处理打进来的电话;初中那会班主任排座位时可能会让成绩好的同学先挑座位。

在这种情况下,数据结构应该提供两个最基本的操作一个是每次优先返回优先级高的对象,一个是添加新的对象。这种数据结构就是优先级队列(Priority Queue)。

2.优先级队列的底层模拟

在JDK1.8中,PriorityQueue底层使用了堆这种数据结构,而堆实际就是在完全二叉树的基础上进行了一些调整。

2.1堆的概念  

在我们之前学习中直到,二叉树可以进行链式存储,而堆本质上也是一种二叉树,不过它的存储方式不在是链式存储,而是顺序存储(存储在数组中)

那么一个数组如何表示一颗二叉树呢?

如图,采用一个层序遍历的方式就可以了:

大家可以在仔细观察一下这棵二叉树有什么特点?

没错,在这颗树中:

1.每个节点的左右两个子节点所储存的值都要比双亲节点(父亲节点)储存的值要大

2.这是一颗完全二叉树

实际上,上面这棵树就是一个

堆分为大根堆 以及 小根堆。

        像这种某个节点的值总是不小于其父节点的值,我们称为小根堆

如图:

另外,某一个节点的值总是不大于其父节点的值,我们称之为大根堆

如图:

注意:只要是堆,那么他一定是完全二叉树

因为堆是顺序存储的,如果不是一颗完全二叉树,会出现这种情况:

数组中会有很多空间没有被利用。

而完全二叉树恰好能避免这种情况,实现数据的高效存储

2.2优先队列的模拟实现


想要实现优先队列,实际上就是要创建一个堆,对堆,进行相关的操作。

不过想要用顺序存储这样一个数据结构,好像有亿点难ε=(´ο`*)))唉。

那么它真的很难吗?

如果你已经学过二叉树的基本操作了,那么你可以大胆的对别人说这个堆,它其实亿点也不难ƪ(˘⌣˘)ʃ


优先级队列的底层是堆

堆的底层就是一个数组

下面以大根堆为例,创建一个数组对数组进行操作,进而实现优先级队列

2.2.1把Heap类定义好

class BigHeap {

    int[] elem;
    int useSize;//堆的大小,默认为0

    public BigHeap() {//调用构造方法,数组默认容量是10,不够之后再考虑扩容
        this.elem = new int[10];
    }

    public void init(int[] array) {

    }

    public void creatHeap() {

    }

    public void siftDonw(int parent, int end) {

    }

    public void offer(int val) {

    }

    public void siftUp(int child) {

    }

    public boolean isFull() {
        return false;
    }

    public boolean isEmpty() {
        return false;
    }

}

下面,我们依次实现各种操作方法:

2.2.2初始化堆

    public void init(int[] array) {
        if(array.length>elem.length){//所给的数组大小,比预先堆中的大,要对堆扩容
            Arrays.copyOf(elem,array.length+elem.length);
        }
        for (int i = 0; i <array.length ; i++) {
            elem[i]=array[i];
            useSize++;//使用量,每次加一
        }
    }

2.2.3创建大堆

在此之前,你要掌握一下知识:

在顺序存储的二叉树中:

parent=(child-1)/2----->不论是左孩子,还是右孩子,计算出的都是他们同一个父节点

leftChild=parent*2+1

rightChild=parent*2+1

1.思路

在初始化的时候,我们已经得到了一个完全二叉树。

想要创建大堆,那么这颗完全二叉树的每个子节点的值都不能大于父节点的值。

其实,实现它也很简单。

第一步:

使用一个算法(向下调整算法),当对某一个节点(子树)完成这个算法的时候,这颗子树就变成了一个大根堆

第二步

从这颗初始化的完全二叉树的最后一个父节点【lastParent=((elem.length-1)-1)/2】开始

使用第一步的算法,然后父节点,在跳转到父节点的父节点继续使用该算法,直到遍历到树的root节点为止。

这时,此树就是大根堆了

以此二叉树为例:

图文理解:

从最后一个父节点(度不为0的)也就是9,到根节点(也就是3)

所以,一共要进行次向下调整算法。

第一次向下调整:

解释:

从9节点开始向下比较。

1. 先对13和12进行比较(先对孩子节点比较)

2. 因为13>12,所以13在对9比较。

3. 9<13,所以9和13要交换


由于如果在往下比较,父节点就没有子节点了,所以直接进行第二次向下调整算法

解释:

Dad倒着往上依次遍历,使用向下调整算法,所以这时,Dad指向7位置

与第一次向下调整算法一样

7和12交换,然后直接进行第三次向下调整算法


第三次向下调整算法:

解释:

Dad倒着往上依次遍历,使用向下调整算法,所以这时,Dad指向5位置

5小于最大的孩子节点13,所以13和5进行交换

如图:

此时我们还在进行第三次向下调整,因为当前的5节点的左右孩子都存在因此还要进行比较

在虚线中三个节点,12最大,12和5交换,然后第三次向下调整结束。


第四次向下调整:

解释:

Dad倒着往上依次遍历,使用向下调整算法,所以这时,Dad指向3位置

上图,解同样地,按部就班,13和3交换

上图同样地,3和12交换

上图同样地,3和9交换


然后大根堆就出来了:

2.思路转化为代码
 public void creatHeap() {

        for (int i =((useSize-1)-1)/2; i >=0 ; i--) {
            siftDown(i,useSize-1);//从后往前,全部使用一次向下调整算法
        }
    }


    private void swap(int a,int b){
        int t;
        t=elem[a];
        elem[a]=elem[b];
        elem[b]=t;
    }
    public void siftDown(int parent, int end) {//parent end都是有效下标

        int child=2*parent+1;//默认是左孩子
        while(child<=end){//调整到最后一个子节点,为止
            //先判断是否有右孩子
            if(child+1<=end){//如果有判断谁大,大的当左孩子
                if(elem[child]<elem[child+1]){
                    swap(child,child+1);
                }
            }

            //左孩子在和父节点进行比较
            if(elem[child]>elem[parent]){//如果孩子节点大,那么父子交换位置
                swap(child,parent);
            }else{
                break;//如果父节点已经是最大的就不用调整了,这棵树就是大根堆
                //因为我们会从后往前,把这棵树(数组)一次遍历调整完
            }
            //下面继续往往下面调整
            parent=child;//当前的父亲,变成自己的孩子
            child=parent*2+1;//孩子变成孩子的孩子
        }
    }

2.2.4堆操作之offer(进队列)

1.思路

把要进队的元素放到数组最后一个元素后面:

他进行向上调整一次就OK喽

假设进队元素==13:

向上调整:

和向下调整一样,只是顺序变成了自上而下。

需要进队的元素先与父节点比较,

如果进队元素更大,就交换,然后子节点变成父节点,父节点,变成父节点的父节点,依次比较到根。

一旦父节点比孩子节点都大,那么就退出

调整后的大根堆:

2.思路转化为代码:
 public void siftUp(int child) {
        int parent=(child-1)/2;
        while(parent>=0){
            if(elem[parent]<elem[child]){//孩子节点,大,那么就交换
                swap(elem[parent],elem[child]);
                child=parent;//向上继续比较
                parent=(child-1)/2;//向上继续比较
            }else{//否则调整完毕,直接跳出(因为本身就是一个大根堆)
                break;
            }
        }
    }

2.2.5堆操作之poll(删除)

注意:堆的删除一定是删除堆顶元素!

思路:

1.堆顶元素和最后一个元素进行交换

2.堆的有效个数减少一个

3.对堆顶元素进行一次向下调整

图片演示:

思路转化为代码:

 public int poll(){
        if(isEmpty())return -1;//如果堆是空的,返回一个无效值

        //删除一定是删除除堆顶元素

        //堆顶元素和最后一个元素交换位置,useSize--,然后对堆顶元素进行一次向下调整即可
        swap(0,useSize-1);

        siftDown(0,useSize-1);

        useSize--;

        return elem[useSize];//返回被poll出来的元素值

    }

2.2.6 isFuLL和isEmpty

两个都很简单,也没什么好说的


    public boolean isFull() {
        if(useSize>=elem.length)return true;
        return false;
    }

    public boolean isEmpty() {
        if(useSize==0)return true;
        return false;
    }

2.3建堆的时间复杂度

考虑最坏情况,那么这颗树就是一个满二叉树,并且是一个小根堆(以大根堆为例)。

进而可以简化证明过程:

当到了第h层时:

有2^(n-1)个节点,要向下移动0层

当然,实际上我们上面的程序是从第h层开始执行的,不过这都一样。

它的时间复杂度就可以变成这样:

通过观察我们发现,这其实就是等差乘等比的形式,错位相减法,就能求出项数和。

最终:建堆的时间复杂度为O(N)


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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

初识java——jdk?环境变量?及关于安装jdk的步骤

文章目录 JDK的安装在安装JDK时遇到的问题&#xff1a; 背景知识一 什么是jdkjdk简介jdk文件详解&#xff1a;1 bin目录&#xff1a;2 lib目录&#xff1a;3 include目录.exe文件是可执行的应用程序&#xff0c;这个我们都清楚&#xff0c;但.dll文件又是做什么的呢&#xff1f…

Advanced RAG 04:重排序(Re-ranking)技术探讨

编者按&#xff1a;重排序&#xff08;Re-ranking&#xff09;技术在检索增强生成&#xff08;Retrieval Augmented Generation&#xff0c;RAG&#xff09;系统中扮演着关键角色。通过对检索到的上下文进行筛选和排序&#xff0c;可以提高 RAG 系统的有效性和准确性&#xff0…

查找算法之顺序查找

目录 前言一、查找算法预备知识二、顺序查找三、总结3.1 查找性能3.2 适用场景 前言 查找算法是一种用于在数据集合中查找特定元素的算法。在计算机科学中&#xff0c;查找算法通常被用于在数组、链表、树等数据结构中查找目标元素的位置或者判断目标元素是否存在。 查找算法的…

爱尔兰启动其首个量子技术国家战略“量子 2030”

内容来源&#xff1a;量子前哨&#xff08;ID&#xff1a;Qforepost&#xff09; 文丨王珩 排版丨沛贤 800字丨5分钟阅读 摘要&#xff1a;爱尔兰推出了“量子 2030”&#xff0c;这是爱尔兰第一个量子技术国家战略。“量子 2030”将爱尔兰量子技术界的努力重点放在爱尔兰可以…

电脑文件加密软件有哪些?文件加密软件哪个好?

某企业的一位员工因不慎将包含敏感客户数据的电脑丢失&#xff0c;导致企业面临巨大的法律风险和经济损失。 这一事件凸显了电脑文件加密的必要性。 如果该企业事先采用了文件加密软件对敏感数据进行保护&#xff0c;即使电脑丢失&#xff0c;攻击者也无法轻易获取到文件内容…

STL_List与萃取

List 参考文章: https://blog.csdn.net/weixin_45389639/article/details/121618243 List源码 List中节点的定义&#xff1a; list是双向列表&#xff0c;所以其中节点需要包含指向前一节点和后一节点的指针&#xff0c; data是节点中存储的数据类型 template <class _Tp&g…

HCIP——MPLS(笔记)

MPLS--多协议标签交换技术 包交换 数据组成数据包&#xff0c;之后&#xff0c;在各个网络节点中不断传递&#xff0c;最终到达目标。包交换转发效率不高的问题所在&#xff1a;1&#xff0c;在整个包交换的过程中&#xff0c;需要先查询路由表之后再查看ARP缓存表两张表来完…

Java:内部类

目录 1.内部类介绍2.实例内部类3.静态内部类4.局部内部类5.匿名内部类 1.内部类介绍 当一个事物的内部&#xff0c;还有一个部分需要一个完整的结构进行描述&#xff0c;而这个内部的完整的结构又只为外部事物提供服务&#xff0c;那么这个内部的完整结构最好使用内部类。在 J…

KDD‘23 | AlphaMix: 高效专家混合框架(MoE)显著提高上证50选股表现

KDD23 | AlphaMix: 高效专家混合框架&#xff08;MoE&#xff09;显著提高上证50选股表现 原创 QuantML QuantML 2024-04-18 09:17 上海 Content 本文提出了一个名为AlphaMix的新型三阶段专家混合&#xff08;Mixture-of-Experts&#xff0c;MoE&#xff09;框架&#xff0c;…

信息流广告大行其是,微博回望“原生”的初心

摘要&#xff1a;有流量的地方&#xff0c;就当有原生信息流广告 信息流广告&#xff0c;自2006年Facebook推出后就迅速火遍全球数字营销界&#xff0c;被誉为实现了广告主、用户、媒体平台三赢。特别是随着OCPM/OCPX大放异彩&#xff0c;信息流广告几乎成为广告主的必选项&…

达梦数据库的AWR报告

达梦数据库的AWR报告 数据库快照是一个只读的静态的数据库。 DM 快照功能是基于数据库实现的&#xff0c;每个快照是基于数据库的只读镜像。通过检索快照&#xff0c;可以获取源数据库在快照创建时间点的相关数据信息。 为了方便管理自动工作集负载信息库 AWR&#xff08;Auto…

数据结构实验(二)

单链表的基本操作 一、总的设计思路(c++实现) 1、首先定义一个包含name、gender、student_number、hobbies的学生信息结构体。 2、接着一一写出:链表初始化(initialize)函数、后插法插入(insert)函数、打印信息(output)函数、对链表结点进行排序(sortList)函数、…

【Qt 学习笔记】Qt常用控件 | 按钮类控件 | Check Box的使用及说明

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt常用控件 | 按钮类控件 | Check Box的使用及说明 文章编号&#xff…

华为ensp中MSTP多网段传输协议(原理及配置命令)

作者主页&#xff1a;点击&#xff01; ENSP专栏&#xff1a;点击&#xff01; 创作时间&#xff1a;2024年4月22日15点29分 在华为ENSP中&#xff0c;MSTP&#xff08;多段传输协议&#xff09;是重要的生成树协议&#xff0c;它扩展了STP&#xff08;生成树协议&#xff09…

跨境电商日报:Tk使用时长全美第一;Shopee发布Z世代购物调查报告

# 平台资讯 PART 1 电商 Shopee调查&#xff1a;六成Z世代购物者看重平台功能多样性 日前&#xff0c;据外媒报道&#xff0c;Shopee发布了《在数字时代吸引Z世代购物者》调查报告。数据显示&#xff0c;60%的Z世代购物者优先考虑搜索简单、具有比较功能和有用评论的平台。据…

代码随想录算法训练营第三十六天|435. 无重叠区间,763.划分字母区间,56. 合并区间

题目&#xff1a;435. 无重叠区间 给定一个区间的集合 intervals &#xff0c;其中 intervals[i] [starti, endi]。返回需要移除区间的最小数量&#xff0c;使剩余区间互不重叠。 题目链接/讲解链接&#xff1a; https://programmercarl.com/0435.%E6%97%A0%E9%87%8D%E5%8F%A0…

Swin Transformer 浅析

Swin Transformer 浅析 文章目录 Swin Transformer 浅析引言Swin Transformer 的网络结构W-MSA 窗口多头注意力机制SW-MSA 滑动窗口多头注意力机制Patch Merging 图块合并 引言 因为ViT无法实现CNN中的层次化构建以及局部信息&#xff0c;由此微软团队提出了Swin Transformer来…

Linux磁盘及读写数据原理/Raid技术/硬软raid及企业案例/磁盘分区环境搭建/格式化磁盘系列-12213字

高薪思维&#xff1a; 怎么才能一直去坚持下去&#xff1f; 1.做这件事情的好处&#xff0c;对自己一直去放大。 2.不做的坏处&#xff0c;并放大 3.学习痛苦&#xff1f;还是去上班&#xff08;餐饮、外卖痛苦&#xff1f;&#xff09; 用比学习更痛苦的事情&#xff0c;去对抗…

Java后端中如何随意接收参数

目录 一、参数名相同 二、参数名不同&#xff0c;使用RequestParam注解 大概访问流程是&#xff1a;先访问test控制器&#xff0c;test控制器跳转到index页面&#xff08;此时index页面收到了test控制器传来的数据&#xff09;&#xff0c;然后在index页面跳转到t5控制器&…

【YOLOv9】实战二:手把手教你使用TensorRT实现YOLOv9实时目标检测(含源码)

‍‍&#x1f3e1;博客主页&#xff1a; virobotics(仪酷智能)&#xff1a;LabVIEW深度学习、人工智能博主 &#x1f384;所属专栏&#xff1a;『LabVIEW深度学习实战』 &#x1f4d1;上期文章&#xff1a;『【YOLOv9】实战一&#xff1a;在 Windows 上使用LabVIEW OpenVINO工具…