Java-数据结构-优先级队列(堆)-(一) (;´д`)ゞ

文本目录:

❄️一、优先级队列:

          ➷ 1、概念:

❄️二、优先级队列的模拟实现:

          ➷ 1、堆的概念:

          ➷ 2、堆的性质:

           ➷ 3、堆的创建:

  ▶ 向下调整:

            ➷ 4、堆的插入和删除:

       ▶ 堆的插入:

 ☞ 思路:

 ☞ 代码:

        ▶ 堆的删除:

 ☞ 思路:

 ☞ 代码:

            ➷ 5、堆的判空和判满:

            ➷ 6、堆的对顶数据:

             ➷ 7、堆的总代码:

❄️总结:


❄️一、优先级队列:

          1、概念:

     我们在前面是介绍过队列的,队列是一种先进先出的数据结构,但是在有些情况下呢,我们操作的数据有优先级,这时候,在我们出队列的时候呢,可能需要先出优先级高的先出队列,那么在这种情况下呢,我们的队列就是有些不合适了,比如呢,我们在 玩游戏的时候呢,来个电话,是不是手机先处理电话,之后再处理游戏去。

      在这种情况下,数据结构提供了两种最基本的操作:一个事返回优先级最高的对象,另一个是添加新的对象。这种操作呢就是 优先级队列——Priority Queue


❄️二、优先级队列的模拟实现:

     在 JDK1.8 中呢,我们的 Priority Queue 的底层使用了堆的数据结构,而对于 堆呢就是在 完全二叉树 的基础上进行一些调整的。

          1、堆的概念:

   如果有一个关键码的集合 K = {K0,K1,K2,.......,Kn - 1},把它的所有数据都按照 完全二叉树的顺序存储方式存储 在一个一维数组中,并且满足:

     Ki <= K2i + 1 且 Ki <= K2i + 2 (Ki >= K2i + 1 且 Ki >= K2i + 2) i = 0,1,2,3,4,5.......,

   则称为 小堆(大堆)。

   在这里我们 将根节点最大的堆称为 最大堆 或者 大根堆

                      将根节点最小的堆称为 最小堆 或者 小根堆


           2、堆的性质:

•  堆中某个节点的值总是 不大于  或者 不小于 其父节点的值。

•  堆是一颗完全二叉树。

我们来看看 大根堆 和 小根堆 的逻辑结构和存储结构:

     我们的存储方式是采用 层序存储 的 规则来进行存储到数组中。这样就非常的高效了

     我们要注意如果不是 完全二叉树 的情况下呢,我们的不能使用 层序遍历的存储规则,这样会浪费空间,所以如果不是完全二叉树不能这样做。


            3、堆的创建:

     我们在创建堆之前呢,我们先来做一些准备工作,我们要注意我们的堆的底层使用的是数组进行实现的。

 我们将这些数据变成 大根堆 或者 小根堆 但是呢,我们要如何才能创建 大根堆 或者 小根堆 呢?

在创建 大根堆 或者 小根堆 之前呢,我们先把这个数组的值呢变成 完全二叉树 来看看:

我们红色的字体就是我们的数组的下标。这个就是我们这组数据的 完全二叉树 的逻辑结构。

 那么我们要如何创建堆呢?这里呢我们要了解到一个知识点——向下调整


  ▶ 向下调整:

      我们这里是 从最后一颗子树进行调整,找到最后一颗子树的根节点,再比较这个根节点的左右节点谁大,我们把根节点和这个最大值进行调整,这样循环调整每一颗子树。这样就可以调整成大根堆。

这里呢,我们直接看图来理解这个问题:

这就是我们的大根堆的调整方法从 完全二叉树 调整。 

这里我们需要一些必须要求的东西:

1、首先要找到最后一颗子树的位置,也就是最后一颗子树的根节点。

     这里我们使用一个公式:(数组的长度 - 1 - 1)/ 2  就是我们的最后一颗子树的根节点。我们使用 parent 标记这个节点。

2、我们如何找到我们的下一颗子树的根节点位置?

      这个就比较简单了,我们直接 parent-- 就可以了。直至我们的 parent 下标 >= 0 即可比如:

3、我们要知道我们的子树的 左子树 ,为什么是左子树呢?因为是由 完全二叉树 调整而来,如果        没有左子树就没有右子树,所以只需要找到左子树即可

      那么怎样去找呢?这里也有公式:child = parent * 2 + 1 ,就是我们左子树。但是这个呢我们还要找其左右子树最大的值,不是直接对child进行交换的并且要注意我们的 child 要 小于 有效的数组长度

 4、我们怎样知道我们的调整结束了?

      这里我们不能调整一次就结束,这里的结束条件就是,当我们的 左子树这个节点的下标比有效数组长度长的时候就是结束的时候了

      每次我们要对 child 和 parent 进行调整。parent = child     child = parent * 2 + 1


我们来演示一变看看如何进行的:

这就是我们的 向下调整  成 大根堆 的操作。来看看这个 向下调整 的代码:

/**** @param parent 每颗子树调整时候的起始位置* @param usedSize 用来判断 每颗子树什么时候结束的*/private void shiftDown(int parent,int usedSize) {int child = parent * 2 + 1;//parent根节点的左子树的节点while (child < usedSize) {//判断有没有右子树,如果有就把 child 设置为最大的值的下标if (child + 1 < usedSize && elem[child + 1] > elem[child]) {//右子树比左子树大把 child + 1,就是右子树child += 1;}if (elem[parent] < elem[child]) {//进行交换swap(elem,child,parent);//调整完,把 parent 和 child 进行调整位置parent = child;child = parent * 2 + 1;}else {//这是 parent下标的值大于child,跳出break;}}}private void swap(int[] elem,int i,int j) {int tmp = elem[i];elem[i] = elem[j];elem[j] = tmp;}

     这个呢就是我们的 向下调整 的 创建大根堆,对于我们的要是 创建 小根堆 的话就是把其小的数值进行调整就可以了,就是反过来进行调整


     我们这个时候来看看对于堆的创建,了解到 向下调整 之后呢,就是非常的容易了,我们呢就是把每一个 parent 进行 向下调整,这里就用到循环遍历就可以了。

     我们来看代码:

//堆的创建public void createHeap() {for (int parent = (this.usedSize - 1 - 1) / 2; parent >= 0 ; parent--) {shiftDown(parent,this.usedSize);}}

Ok啊,这样我们的 大根堆的创建 就创建完成了。


             4、堆的插入和删除:

       ▶ 堆的插入:

 ☞ 思路:

      对于我们的堆的插入的操做呢,我们的插入一定是在最后一个位置插入的,这个位置,我们一定是知道的,就是我们的 usedSize 这个下标的位置插入数据。我们就可以根据这个位置求出其插入的值的根节点的位置。

       我们来看图来理解:

    这个 90 就是我们新插入的值,我们再对其进行 向上调整,我们是知道插入值的下标,就可以计算到根节点的下标,就可以进行 向上调整了 。 

再对其进行向上调整就可以了。 

 

这里我们也要注意当我们的数组使用满了后,我们就需要扩容了。

这个就是插入的操作了。我们来看看代码是如何编写的: 

 ☞ 代码:
//堆的插入public void offer(int val) {if (usedSize == elem.length) {//满了。2倍扩容this.elem = Arrays.copyOf(this.elem, 2 * this.elem.length);}//插入操作this.elem[usedSize] = val;//这个时候 usedSize 位置就是我们的子树的坐标位置shiftUp(usedSize);this.usedSize++;}private void shiftUp(int child) {int parent = (child - 1) / 2;while (parent >= 0) {//进行向上调整if (this.elem[parent] < this.elem[child]) {//交换swap(elem,child,parent);//parent 和 child 调整位置child = parent;parent = (child - 1) / 2;}else {break;}}}

OK,这个呢就是我们的插入操作的代码了之后我们来看看删除操作是如何做到的。 


        ▶ 堆的删除:

 ☞ 思路:

    这个删除操作还是很简单的,我们需要删除优先级高的数据,所以我们删除的就是 0 下标的值

我们呢有三个步骤:

1、把堆的 0 下标的数据 和 usedSize - 1 这个下标的数据进行交换,就是第一个数据和最后一个         数据进行交换。

2、我们把有效数组长度减一。

3、我们就是 0 这个下标不是大根堆的结构,所以我们对 0 这个下标进行 向下调整 操作。

这个呢就是我们删除的思路了,我们接下来看看其代码: 

 ☞ 代码:
//堆的删除操作public int poll() {//删除优先级最高的就是0下标的值,有三个步骤://1、把堆的第一个数据和最后一个数据进行交换//2、把有效数据长度进行减1//3、把 0 下标的值进行向下调整if (usedSize == 0) {//堆为空,结束return -1;}int val = elem[0];//1、swap(elem,0,usedSize - 1);//2、usedSize--;//3、shiftDown(0,usedSize);return val;}

             5、堆的判空和判满:

这两个操作就非常的简单了,我们直接来看代码:

    //判空public boolean isEmpty() {return usedSize == 0;}//判满public boolean isFull() {return usedSize == this.elem.length;}

             6、堆的对顶数据:

这个同样非常的简单,我们直接返回下标为 0 的值就可以了。

    //返回堆顶的数据public int peek() {if (isEmpty()) {return -1;}return this.elem[0];}

这样呢我们所有的基本操作就都完成了,我们来看一下总代码: 


              7、堆的总代码:

import java.util.Arrays;public class TestHeap {public int[] elem;public int usedSize;public TestHeap() {//初始话this.elem = new int[10];}public void initElem(int[] array) {//把elem里的值设置为我们输入的值for (int i = 0; i < array.length; i++) {elem[i] = array[i];}}/*** 建堆的时间复杂度为O(N)。* 大根堆* @param parent 每颗子树调整时候的起始位置* @param usedSize 用来判断 每颗子树什么时候结束的*/private void shiftDown(int parent,int usedSize) {int child = parent * 2 + 1;//parent根节点的左子树的节点while (child < usedSize) {//判断有没有右子树,如果有就把 child 设置为最大的值的下标if (child + 1 < usedSize && elem[child + 1] > elem[child]) {//右子树比左子树大把 child + 1,就是右子树child += 1;}if (elem[parent] < elem[child]) {//进行交换swap(elem,child,parent);//调整完,把 parent 和 child 进行调整位置parent = child;child = parent * 2 + 1;}else {//这是 parent下标的值大于child,跳出break;}}}private void swap(int[] elem,int i,int j) {int tmp = elem[i];elem[i] = elem[j];elem[j] = tmp;}//大根堆的创建public void createHeap() {for (int parent = (this.usedSize - 1 - 1) / 2; parent >= 0 ; parent--) {shiftDown(parent,this.usedSize);}}//堆的插入public void offer(int val) {if (isFull()) {//满了。2倍扩容this.elem = Arrays.copyOf(this.elem, 2 * this.elem.length);}//插入操作this.elem[usedSize] = val;//这个时候 usedSize 位置就是我们的子树的坐标位置shiftUp(usedSize);this.usedSize++;}private void shiftUp(int child) {int parent = (child - 1) / 2;while (parent >= 0) {//进行向上调整if (this.elem[parent] < this.elem[child]) {//交换swap(elem,child,parent);//parent 和 child 调整位置child = parent;parent = (child - 1) / 2;}else {break;}}}//堆的删除操作public int poll() {//删除优先级最高的就是0下标的值,有三个步骤://1、把堆的第一个数据和最后一个数据进行交换//2、把有效数据长度进行减1//3、把 0 下标的值进行向下调整if (isEmpty()) {//堆为空,结束return -1;}int val = elem[0];//1、swap(elem,0,usedSize - 1);//2、usedSize--;//3、shiftDown(0,usedSize);return val;}//返回堆顶的数据public int peek() {if (isEmpty()) {return -1;}return this.elem[0];}//判空public boolean isEmpty() {return usedSize == 0;}//判满public boolean isFull() {return usedSize == this.elem.length;}
}

❄️总结:

     OK,这篇博客呢就到这里就结束了,这篇博客我们介绍的是 优先级队列 的底层操作代码,下一篇博客我们来看看 Java 自带的 优先级队列吧~~并且还有一些关于我们的优先级队列的习题,敬请期待吧!!!拜拜·~~~

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

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

相关文章

Linux权限管理: 文件访问者分类、文件类型和访问权限、权限值的表示方法、文件访问权限的相关设置方法、umask、目录的权限、粘滞位等的介绍

文章目录 前言一、文件访问者分类二、文件类型和访问权限1. 文件类型2. 基本权限 三、权限值的表示方法1. 字符表示方法2. 8进制数值表示方法 四、文件访问权限的相关设置方法1. chmod2. chown3. chgrp 五、 umask六、目录的权限七、粘滞位总结 前言 Linux权限管理&#xff1a…

论文不会写?分享6款AI论文写作免费一键生成网站!

在当今学术研究和写作领域&#xff0c;AI论文写作工具的出现极大地提高了写作效率和质量。这些工具不仅能够帮助研究人员快速生成论文草稿&#xff0c;还能进行内容优化、查重和排版等操作。本文将分享6款免费一键生成AI论文写作网站&#xff0c;并重点推荐千笔-AIPassPaper。 …

鸿蒙OpenHarmony【轻量系统芯片移植】物联网解决方案之芯海cst85芯片移植案例

物联网解决方案之芯海cst85芯片移植案例 本文介绍基于芯海cst85芯片的cst85_wblink开发板移植OpenHarmony LiteOS-M轻量系统的移植案例。开发了Wi-Fi连接样例和XTS测试样例&#xff0c;同时实现了wifi_lite, lwip, startup, utils, xts, hdf等部件基于OpenHarmony LiteOS-M内核…

初学者如何快速入门大语言模型(LLM)?

初学者如何快速入门大语言模型&#xff08;LLM&#xff09; 知乎大佬已给出了比较合理的方案&#xff0c;小白千万别走弯路了&#xff0c;下面给大家梳理和解读&#xff1a; 技术要求&#xff1a;要入门大语言模型&#xff0c;需要掌握以下基本技术&#xff1a; 开发语言&…

模拟实现string类: clear函数、流提取(<<)和流插入(>>)运算符重载、>、<、==、<=、>=、!=的运算符重载、赋值运算符(=)重载等的介绍

文章目录 前言一、 clear函数二、流提取(<<)和流插入(>>)运算符重载三、 >、<、、<、>、!的运算符重载四、赋值运算符&#xff08;&#xff09;重载总结 前言 模拟实现string类: clear函数、流提取(<<)和流插入(>>)运算符重载、>、<…

上线跨境电商商城的步骤

上线一个跨境电商商城涉及多个步骤&#xff0c;从前期准备到上线后的维护。以下是一些关键步骤&#xff1a; 1. 市场调研与规划 目标市场分析&#xff1a;研究目标市场的需求、竞争对手和消费者行为。法律法规&#xff1a;了解并遵守目标市场的法律法规&#xff0c;包括税收、…

git学习【持续更新中。。。】

git学习【持续更新中。。。】 文章目录 git学习【持续更新中。。。】一、Git基本操作1.创建本地仓库2.配置本地仓库1.局部配置2.全局配置 3.认识工作区、暂存区、版本库4.添加文件5.修改文件6.版本回退7.撤销修改8.删除文件 二、Git分支管理1.理解分支2.创建、切换、合并分支3.…

YOLOv8改进:SA注意力机制【注意力系列篇】(附详细的修改步骤,以及代码,与其他一些注意力机制相比,不仅准确度更高,而且模型更加轻量化。)

如果实验环境尚未搭建成功&#xff0c;可以参考这篇文章 ->【YOLOv8超详细环境搭建以及模型训练&#xff08;GPU版本&#xff09;】 文章链接为&#xff1a;http://t.csdnimg.cn/8ZmAm ---------------------------------------------------------------------------​---…

亲测有效,长期有效的RTSP流地址公网RTSP地址,各种类型的视频源

我们经常需要做一些实时视频流的测试&#xff0c;但是手边又没有办法及时弄到一个摄像机&#xff0c;我们经常会去搜索一下“公网RTSP地址”&#xff0c;但是大部分现在都失效了&#xff0c;有什么办法能够让我们快速构建一个RTSP流&#xff0c;点几下就能直接用&#xff1f; …

C++ 在项目中使用Linux命令

一: 选择shell Linux 命令是由shell解析并转发给操作系统执行的&#xff0c;所有的shell都是从 Bourne shell&#xff08;/bin/sh&#xff09;派生的&#xff0c;Bourne shell是贝尔实验室为早期版本的Unix开发的标准shell。 每个Unix系统都需要一个版本的Bourne shell才能正…

Maple常用命令

1. 重启内核&#xff1a; restart 2. 化简式子 simplify(式子) 3. 引用前面出现的公式&#xff1a; CtrlL&#xff0c;在弹出的以下对话框中输入要引用的公式编号 4.

Pandas的读写数据

目录 读写文件的类型 Excel写 API 准备数据 1.直接写入(默认有索引和标题) 2.写入(去掉索引) 3.写入(去掉索引和标题) 4.写入(去掉索引和标题,指定表单信息) Excel读 API 1.读(默认带有索引和标题) 2.读(指定索引项) 3.读(碰到无标题列和无索引列,指定索引列,标题列…

[SIGGRAPH-24] CharacterGen

[pdf | code | proj] LRM能否用于3D数字人重建&#xff1f;问题在于&#xff1a;1&#xff09;缺少3D数字人数据&#xff1b;2&#xff09;重建任意姿态的3D数字人不利于后续绑定和驱动。构建3D数字人数据集&#xff1a;在VRoidHub上采集数据&#xff0c;得到13746个风格化角色…

vue3中如何拿到vue2中的this

vue3中常用api vue3中常用响应式数据类型&#xff1a;

python3GUI--字符串加密方案(附源码)

文章目录 一&#xff0e;前言二&#xff0e;展示1.AES 加密1.介绍优点缺点2.代码3.结果 2.RSA 加密1.介绍优点缺点2.代码3.结果 3.基于 HMAC 的 URL 签名1.介绍优点缺点2.代码3.结果 4.JWT&#xff08;JSON Web Token&#xff09;加密1.介绍优点缺点2.安装3.代码4.结果 三&…

python运行时错误:找不到fbgemm.dll

python运行时错误&#xff1a;找不到fbgemm.dll 报错&#xff1a; OSError: [WinError 126] 找不到指定的模块。 Error loading "D:\program\py\312\Lib\site-packages\torch\lib\fbgemm.dll" or one of its dependencies. 原因是Windows下缺失&#xff1a;libomp140…

Aegisub字幕自动化及函数篇(图文教程附有gif动图展示)(一)

目录 自动化介绍 bord 边框宽度 随机函数 fsvp 随机颜色 move 自动化介绍 自动化介绍:简单来说自动化能让所有字幕行快速拥有你指定的同一种特效 对时间不同的行应用相同的效果 只要设计好一个模板&#xff0c;然后让所有行都执行这个模板上的特效就好了 首先制作模板行…

十八,Spring Boot 整合 MyBatis-Plus 的详细配置

十八&#xff0c;Spring Boot 整合 MyBatis-Plus 的详细配置 文章目录 十八&#xff0c;Spring Boot 整合 MyBatis-Plus 的详细配置1. MyBatis-Plus 的基本介绍2. Spring Boot 整合 MyBatis Plus 的详细配置3. Spring Boot 整合 MyBatis plus 注意事项和细节4. MyBatisx 插件的…

【论文阅读】3D Diffuser Actor: Policy Diffusion with 3D Scene Representations

Abstract 扩散policies是条件扩散模型&#xff0c;它学习以机器人和环境状态为条件的机器人动作分布。他们最近被证明优于确定性和替代的动作分布学习公式。3d机器人policies使用使用感知深度从单个或多个摄像机视图聚合的3d场景特征表示。它们已被证明比它们在相机视点上的 2…

反转字符串 II--力扣541

反转字符串 II 题目思路代码 题目 思路 本题的关键在于理解每隔 2k 个字符的前 k 个字符进行反转&#xff0c;剩余字符小于 2k 但大于或等于 k 个&#xff0c;则反转前 k 个字符。并且剩余字符少于 k 个&#xff0c;则将剩余字符全部反转。 让i每次跳2k&#xff0c;成为每一次…