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

目录

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;//堆的大小,默认为0public 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)


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

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

相关文章

机器学习-10-基于paddle实现神经网络

文章目录 总结参考本门课程的目标机器学习定义第一步&#xff1a;数据准备第二步&#xff1a;定义网络第三步&#xff1a;训练网络第四步&#xff1a;测试训练好的网络 总结 本系列是机器学习课程的系列课程&#xff0c;主要介绍基于paddle实现神经网络。 参考 MNIST 训练_副…

【Node.js】01 —— fs模块全解析

&#x1f525;【Node.js】 fs模块全解析 &#x1f4e2; 引言 在Node.js开发中&#xff0c;fs模块犹如一把万能钥匙&#xff0c;解锁着整个文件系统的操作。从读取文件、写入文件、检查状态到目录管理&#xff0c;无所不能。接下来&#xff0c;我们将逐一揭开fs模块中最常用的那…

vue ant form validate如何对数组下的表单校验

问题 使用Ant Design Vue校验表单时&#xff0c;通过validateFields&#xff0c;但是如何一个数组内部的校验呢&#xff1f; 效果图&#xff1a; 实现方式&#xff1a; 通过 v-for 循环渲染:name"[]"实现&#xff0c;我们直接看代码。 <template><a-for…

Spring Boot中JUnit 4与JUnit 5的如何共存

文章目录 前言一、先上答案二、稍微深入了解2.1 maven-surefire-plugin是什么2.2 JUnit4和JUnit5有什么区别2.2.1 不同的注解2.2.2 架构 前言 在maven项目中&#xff0c;生成单测时是否有这样的疑问&#xff1a;该选JUnit4还是JUnit5&#xff1f;在执行 mvn test 命令时有没有…

三、SpringBoot整合MyBatis

本章节主要描述MyBatis的整合&#xff0c;以及使用mybatis-generator-maven-plugin生成代码骨架&#xff0c;源码&#xff1a; jun/learn-springboot - Gitee.com 一、首先建数据库 本示例用的是MySQL8.0.23&#xff0c;建表t_goods、t_orders&#xff0c;略... 二、goods模块…

Java | Leetcode Java题解之第36题有效的数独

题目&#xff1a; 题解&#xff1a; class Solution {public boolean isValidSudoku(char[][] board) {int[][] rows new int[9][9];int[][] columns new int[9][9];int[][][] subboxes new int[3][3][9];for (int i 0; i < 9; i) {for (int j 0; j < 9; j) {char …

随机森林原理及应用

目录 一、随机森林原理、优点、应用场景 1.1基本原理 1.2主要优点 1.3使用场景 二、具体实例 一、随机森林原理、优点、应用场景 随机森林是一种流行且强大的机器学习算法&#xff0c;属于集成学习方法的一部分&#xff0c;主要用于分类和回归任务。它通过组合多个决策树…

SSTV音频转图片

SSTV工具有很多&#xff0c;这里使用RX-SSTV慢扫描工具 下载安装 RX-SSTV解码软件 下载地址&#xff1a;https://www.qsl.net/on6mu/rxsstv.htm 一直点下一步&#xff0c;安装成功如下图: 虚拟声卡e2eSoft 由于SSTV工具是根据音频传递图片信息&#xff0c;正常解法需要一…

在【laravel框架】学习中遇到的常见的问题以及解决方法

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

Marching Cubes算法

Marching Cubes算法 1. 简介2. 算法原理的理解2.1 如何找到面经过的这些小块(六面体)&#xff1f;2.2 找到后&#xff0c;如何又进一步的找到面与这些小块(六面体)的交点&#xff1b;2.3 这些交点按照怎么的拓扑连接关系连接&#xff0c;是怎么操作的&#xff1f; 3. 总结4. 参…

金融时报:波场亮相哈佛大学并举办TRON Builder Tour活动

近日,波场TRON作为顶级白金赞助商出席哈佛区块链会议并成功举办TRON Builder Tour哈佛站活动,引发海外媒体热议。美联社、金融时报、Cointelegraph等国际主流媒体及加密知名媒体均对此给予了高度评价,认为本次大会对TRON Builder Tour活动具有里程碑意义,彰显了波场TRON致力于促…

mysql基础5——设置主键

业务字段尽量不要用做主键 删除主键&#xff0c;只是主键被删除&#xff0c;字段还存在 alter table demo.membermaster drop primary key; 添加一个字段设置为主键并给主键添加自增约束 alter table demo.membermaster add column id int primary key auto_increment; 自增…

Gitea 简单介绍、用法以及使用注意事项!

Gitea 是一个轻量级的代码托管解决方案&#xff0c;它提供了一个简单而强大的平台&#xff0c;用于托管和协作开发项目。基于 Go 语言编写&#xff0c;与 GitLab 和 GitHub Enterprise 类似&#xff0c;但专为自托管而设计。以下是对 Gitea 的详细介绍&#xff0c;包括常用命令…

anaconda配置的环境对应的地址查看,环境安装位置

打开conda指令窗口 这个和上面的都一样&#xff0c;哪个都行 点开后&#xff0c;输入 conda env list 这里显示的就是自己的每个环境对应的地址了

游戏黑灰产识别和溯源取证

参考&#xff1a;游戏黑灰产识别和溯源取证 1. 游戏中的黑灰产 1. 黑灰产简介 黑色产业&#xff1a;从事具有违法性活动且以此来牟取利润的产业&#xff1b; 灰色产业&#xff1a;不明显触犯法律和违背道德&#xff0c;游走于法律和道德边缘&#xff0c;以打擦边球的方式为“…

巧用断点设置查找bug【debug】

默认设置的断点&#xff0c;当代码运行到断点处MCU就会被挂起&#xff0c;从而停在断点处。 但在某些情况下&#xff0c;如调试FCCU时&#xff0c;如果设置断点&#xff0c;MCU停下后将会导致 FCCU 配置WDG超时。或在调试类似电机控制类的应用时&#xff0c;不适当的断点会导 致…

复合升降机器人教学科研平台——技术方案

一&#xff1a;功能概述 1.1 功能简介 复合升降机器人是一款集成移动底盘、机械臂、末端执行器、边缘计算平台等机构形成的教学科研平台&#xff0c;可实现机器人建图导航、路径规划&#xff0c;机械臂运动学、动力学、轨迹规划、视觉识别等算法功能和应用&#xff0c;提供例如…

Python中列表数据的保存与读取:以txt文件为例

目录 引言 一、列表数据的保存 二、列表数据的读取 三、进阶用法与注意事项 1. 处理嵌套列表 2. 处理大量数据 3. 注意事项 四、总结 引言 在Python编程中&#xff0c;我们经常需要处理各种类型的数据&#xff0c;包括列表。列表是一种非常灵活的数据结构&#xff0c;…

边缘计算的优势

边缘计算的优势 边缘计算是一种在数据生成地点附近处理数据的技术&#xff0c;而非传统的将数据发送到远端数据中心或云进行处理。这种计算模式对于需要快速响应的场景特别有效&#xff0c;以下详述了边缘计算的核心优势。 1. 降低延迟 边缘计算通过在数据源近处处理数据&…

imx6ull设备树驱动--pinctl、ioctl

添加pinctl节点 进入arch/arm/boot/dts目录下dts文件 在iomuxc下添加pinctlled节点 将 GPIO1_IO03 这个 PIN 复用为 GPIO1_IO03&#xff0c;电气属性&#xff08;配置GPIO一些列寄存器&#xff09;值为 0X10B0 添加led设备节点 与上一节一样&#xff0c;在 / 下面添加设备节…