优先级队列(堆)学的好,头发掉的少(Java版)

本篇会加入个人的所谓鱼式疯言

❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言

而是理解过并总结出来通俗易懂的大白话,

小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的.

🤭🤭🤭可能说的不是那么严谨.但小编初心是能让更多人能接受我们这个概念 !!!

在这里插入图片描述

我们深知,在这个多元化的时代,每个人的兴趣与偏好都独一无二。

因此,我们精心挑选了各类题材,从深邃的宇宙奥秘到细腻的日常生活琐事,从古老的文明遗迹到未来的科技幻想,力求满足每一位读者的好奇心与求知欲。

我们相信,每一个有趣的灵魂都能在这里找到属于自己的那片天空,与作者进行跨越时空的对话,享受阅读带来的纯粹快乐。

前言

提及优先级队列,就会 回忆起我们之前学习过的队列

并且我们提及过 队列 ,队列是一种 先入先出 的数据结构, 但是不能考虑数据本身优先级的高低,那该怎么办呢?

在上篇文章中我们主要讲解了传说中 地狱级别难 其实 也没有那么难 的的二叉树, 而在本篇中也是讲解和二叉树相同的 树状的结构 的 优先级队列,我们也称之为 堆 的一种 数据结构 。

提及难度的话,小伙伴这点可以放心, 当然是不及 二叉树 的 💖 💖 💖 💖

关于 堆 的定义和特性,如何创建 堆 并通过调整维护好这个 堆 的各种方法,小编都会在本篇文章中重点讲解。

目录

  1. 堆的初识

  2. 堆的调整

  3. 堆的数据插入和删除

  4. 堆实现优先级队列

一. 堆的初识

是否有以一种 数据结构 是可以考虑优先级的,就是说当我们需要对某个数据或某个事务进行考虑,就可以做到优先执行

比如当小伙伴打游戏时,把优先级设置为最大的,如果有电话过来,就会优先选择电话接听的提示

、
比如小伙伴正在上课, 不想有消息发过来,就会把消息通知设置为优先级最小的, 这样即使有消息发送过来,也不会受到提示消息。

那么就是它了 , 我们的 优先级队列(堆) 就可以在我们的根节点表示优先级最大或最小的,就可以进行优先级最大或最小的数据的管理。

1. 堆的简介

<1>. 概念

像上面这种能够有 优先级特性 的,并且 返回 优先级对象 ,并且能够 插入新对象 的我们称之为 优先级队列(堆) 。

2. 存储方式

在我们上一篇二叉树的学习当中,二叉树存储是用链式结构

堆的存储方式是顺序结构,也就是说它这些有着优先级的数据是存储在一个一位数组中的。

在这里插入图片描述

存储方式有了,我们就需要根据父节点和子节点之间的关系来接替的进行遍历。

具体关系

假设 i 的起始下标为 0

当 i 为子节点 且 i 不为 0 时,我们可以确定 父节点 为 (i - 1)/ 2

当 i 为子节点且 2 * i + 1 不超过最大节点数的下标, 2 * i + 1 为 左子节点

当 i 为子节点且 2 * i + 2 不超过最大节点数 的下标, 2 * i + 2 为 右子节点

鱼式疯言

JDK1.8 的源码中就有 PriorityQueue 这个类为优先级队列,

3. 堆的特性

  1. 根节点大于或小于子节点

  2. 是一颗完全二叉树

1) 根节点大于或小于子节点

我们必须明确的是,只有 根节点 都小于或大于其两边的 子节点 (两边的叶子节点都存在时)

才能实现我们的 优先级对象的返回 。

2) 一颗完全二叉树

上面我们提及堆自身的存储是顺序结构存储的, 所以我们就要构造一颗完全二叉树来管理我们的堆

什么居然有小伙伴们不知道完全二叉树是什么?

完全二叉树学习链接

在这里插入图片描述

如果不是一颗完全二叉树时,就会出现在一维数组中 有部分为null 的情况

就会出现如下情况 🦊🦊🦊🦊

在这里插入图片描述

鱼式疯言

  1. 解释

这里我们指的 完全二叉树 说的不是我们上篇学习过的二叉树, 而是一种 树型结构 哦,小伙伴们一定要搞清楚。

  1. 所以上图告诉 小伙伴们

对于 非完全二叉树 来说

因为是 从上往下, 从左往右 存储在 一维数组 中的,就会出现数据分布比较散乱, 既不好集中管理 ,也会 浪费空间 。

所以我们的顺序存储的 堆 就必须严格保证是 完全二叉树

二. 堆的调整

对于堆本身来说,要符合他优先级大或者小的特性,我们就需要通过一些方法来实现。

常用的有两种方法

  1. 向下调整

  2. 向上调整

1 . 向下调整

在这里插入图片描述

对于向下调整的规则

以 小根堆 为例

  1. 找出左节点和右节点 两者较小的节点 (如果都存在 的话,不可能只出现右节点,所有只会出现左节点,并且左节点就为最小的 )

  2. 如果 父节点 比 子节点 中较小的那个 还小 就成立
    否则就讲父节点和 较小节点 进行交换

  3. 向下调整,讲父节点修改为之前 较小子节点 的位置(child的位置) ,子节点向下走到该子节点的位置 (2*child+1的位置) 再循环进行 比对和调整。

终止条件

  1. 父节点都要比当前 左右子节点都小

  2. 遍历完 所有 的 非叶子节点

请添加图片描述

2. 向上调整

对于向上调整,整体的思路和向上调整差不多

还是以小根堆为例

  1. 找出左节点和右节点 两者较小的节点 (如果都存在 的话,不可能只出现右节点,所有只会出现左节点,并且左节点就为最小的 )

  2. 如果 父节点 比 子节点 中较小的那个 还小 就成立
    否则就讲父节点和 较小节点 进行交换

主要区别:
3. 让子节点成为旧父节点, 让旧父节点修改成自身的父节点 (2*parent+1) 。

终止条件

  1. 父节点都小于左右子节的值

  2. 父节点 < 0

请添加图片描述

三. 堆的数据插入和删除

我们清楚在队列中我们就有增添(插入)数据, 删除数据。

而我们的堆同时也有 相同的功能 。

1. 数据的插入

<1>. 原理剖析

堆的数据插入必然是在一维数组的最后一位进行插入

那么问题来咯,如果我们插入之后,会不会影响堆的优先级呢,答案是肯定的

所以当我们插入一个数据后,就需要调整,那么我们上面学习过两种调整的方法,该适用于哪一种呢?

相比聪明的小伙伴已经想到了,当然是我们的 向上调整 ,原因很简单

我们是在最后插入的,当我们需要数据的 大小足够大 时,就需要不断的向上调整 ,找到该数组在 完全二叉树中 所处的位置。

<2>. 代码展示

  /*** 入队:仍然要保持是大根堆* @param val* 先插入到堆尾* 利用向上调整为大根堆*/public void push(int val) {if (isFull()) {elem= Arrays.copyOf(elem,2*elem.length);}elem[usedSize]=val;usedSize++;int child= usedSize-1;shiftUp(child);}/*** 想上调整为* @param child 孩子节点*  向上调整*  时间复杂度为 o(N*log(N))*/private void shiftUp(int child) {int parent=(child-1)/2;while (parent >= 0 && elem[child] > elem[parent])  {swapElem(elem,parent,child);child=parent;parent=(parent-1)/2;}}public boolean isFull() {return usedSize==elem.length;}

在这里插入图片描述

请添加图片描述

2. 数据的删除

优先级队列的数据删除,是一种比较巧妙的方式

<1>. 原理剖析

我们既要做到堆顶元素的删除 ,也要保证元素的 个数减少

联想我们学习 顺序表 时,删除最后一个元素只需要把数组 大小-1 即可

那么我们删除堆中的数据,不妨就可以先把 最后一个元素 和 堆顶的第一个元素 进行 交换 ,然后再利用顺序表中的 删除方式 来进行 元素的删除 。

当我们 删除完最后一个元素 也就是堆顶元素时,我们的优先级是不是也发生了改变, 答案也是肯定的

那么我们只需要把最开始我们换到 堆 的那个元素,进行 向下调整 即可,根据 元素大小 的 向下调整 到处在符合条件的优先级位置。

<2>. 代码展示

/*** 出队【删除】:每次删除的都是优先级高的元素* 仍然要保持是大根堆*/public int pollHeap() {if (isEmpty()) {return -1;}int ret=elem[0];swapElem(elem,0,usedSize-1);usedSize--;shiftDown(0,usedSize);return  ret;}public boolean isEmpty() {return usedSize==0;}

在这里插入图片描述

请添加图片描述

四. 堆实现优先级队列

上面对于堆的核心分析
小编认为已经差不多了,下面该是小伙伴们自己 动手实践 的过程了

代码就贴在这里了,小伙们自取哦 💖 💖 💖 💖

1. 代码展示

package PriorityQueue;import java.util.Arrays;/*** Created with IntelliJ IDEA.* Description:* User:周次煜* Date: 2024-04-13* Time:13:39*/
public class MyPriorityQueue {public int[] elem;public int usedSize;private  static  final  int FAULTMAXSIZE=10;public MyPriorityQueue() {elem=new int[FAULTMAXSIZE];}/*** 建堆的时间复杂度:** @param array*/public void createHeap(int[] array) {usedSize=array.length;// 初始化堆for (int i = 0; i < usedSize; i++) {elem[i]=array[i];}int len= usedSize;int pareindex=(usedSize-2)/2;// 调整为大堆for (int i =pareindex ; i >= 0 ; i--) {shiftDown(i,len);}}/**** @param parent 是每棵子树的根节点的下标* @param len  是每棵子树调整结束的结束条件* 向下调整的时间复杂度:O(logn)*/private void shiftDown(int parent,int len) {int child=parent*2+1;while (child+1 <len && elem[child] > elem[parent]) {if (elem[child] < elem[child+1]) {child++;}swapElem(elem,child,parent);parent=child;child=child*2+1;}}private  void  swapElem(int []array,int begin,int end ) {int tmp=array[begin];array[begin]=array[end];array[end]=tmp;}/*** 入队:仍然要保持是大根堆* @param val* 先插入到堆尾* 利用向上调整为大根堆*/public void push(int val) {if (isFull()) {elem= Arrays.copyOf(elem,2*elem.length);}elem[usedSize]=val;usedSize++;int child= usedSize-1;shiftUp(child);}/*** 想上调整为* @param child 孩子节点*  向上调整*  时间复杂度为 o(N*log(N))*/private void shiftUp(int child) {int parent=(child-1)/2;while (parent >= 0 && elem[child] > elem[parent])  {swapElem(elem,parent,child);child=parent;parent=(parent-1)/2;}}public boolean isFull() {return usedSize==elem.length;}/*** 出队【删除】:每次删除的都是优先级高的元素* 仍然要保持是大根堆*/public int pollHeap() {if (isEmpty()) {return -1;}int ret=elem[0];swapElem(elem,0,usedSize-1);usedSize--;shiftDown(0,usedSize);return  ret;}public boolean isEmpty() {return usedSize==0;}/*** 获取堆顶元素* @return 返回该对顶第一个元素*/public int peekHeap() {if (isEmpty()) {return -1;}return elem[0];}public  int size() {return usedSize;}
}
public class TestPriorityQueue {public static void main(String[] args) {int[] array = {2, 4, 5, 8, 9, 10, 11};MyPriorityQueue mpq = new MyPriorityQueue();mpq.createHeap(array);mpq.push(18);mpq.push(19);System.out.println("======抛出堆顶元素=======");System.out.println(mpq.pollHeap());System.out.println(mpq.pollHeap());System.out.println("========查看堆顶元素=======");System.out.println(mpq.peekHeap());System.out.println(mpq.peekHeap());System.out.println(mpq.peekHeap());}
}

在这里插入图片描述

鱼式疯言

向上面不仅介绍了直接对 原数组 进行 建堆的方式

   public void createHeap(int[] array) {usedSize=array.length;// 初始化堆for (int i = 0; i < usedSize; i++) {elem[i]=array[i];}int len= usedSize;int pareindex=(usedSize-2)/2;// 调整为大堆for (int i =pareindex ; i >= 0 ; i--) {shiftDown(i,len);}}

这个的 时间复杂度 为 O( N )

而直接插入的时间复杂度为 O(N*log(N))

public void push(int val) {if (isFull()) {elem= Arrays.copyOf(elem,2*elem.length);}elem[usedSize]=val;usedSize++;int child= usedSize-1;shiftUp(child);}

综上所得,建堆的时间复杂度 是 优于 我们的一个一个 插入的时间复杂度 的。

总结

  • 堆的初识: 我们认识到了堆本质上一中有着优先级的, 并且融合了完全二叉树和队列的特性,用顺序存储, 一种特殊的树状结构。

  • 堆的调整: 向上调整和向下调整各种细节和调整顺序

  • 堆的数据插入和删除: 对于插入的场景我们一般用向上调整,对于删除场景, 我们一般向下调整。

  • 堆实现优先级队列 : 从大局中我们用堆实现了优先级队列, 并且从时间复杂度的角度来看,建堆比堆中插入元素更高效。

如果觉得小编写的还不错的咱可支持 三连 下 (定有回访哦) , 不妥当的咱请评论区 指正

希望我的文章能给各位宝子们带来哪怕一点点的收获就是 小编创作 的最大 动力 💖 💖 💖

在这里插入图片描述

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

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

相关文章

问界3D-为什么在建模的过程中要先映射再展开呢

问题提出&#xff1a;为什么在建模的过程中要先映射再展开呢 在建模过程中先进行映射再展开的原因主要涉及到工作流程的效率和纹理质量的保证。具体来说&#xff0c;以下几点解释了为什么这一顺序是合理且常见的&#xff1a; 1. 初步确定UV布局 属 性描述映射通过不同类型的映…

Java Stream API 常用操作技巧

Java 8 引入的 Stream API 为集合操作提供了一种声明式编程模型&#xff0c;极大地简化了数据处理的复杂性。本文将介绍 Java Stream API 的几种常用操作方式&#xff0c;帮助开发者更高效地处理集合数据。 1. 过滤&#xff08;Filtering&#xff09; 过滤是选择集合中满足特…

十大全球网络安全巨头面向平台化和生成式AI的关键举措:收购、整合和建立战略联盟

随着人工智能和生成人工智能能力的不断增强&#xff0c;从点解决方案向整合和平台的转变正在重新定义 IT 安全市场及其领先的供应商。&#xff08;以下内容翻译自CSO在线网站&#xff09; 攻击者一直在不断进步&#xff0c;现在他们可以使用生成式AI 来帮助制作勒索软件电子邮…

如何改变网络ip地址?轻松掌握的实用方法

在数字化时代&#xff0c;网络IP地址作为设备在网络中的唯一标识&#xff0c;扮演着至关重要的角色。然而&#xff0c;在某些情况下&#xff0c;我们可能需要更改设备的IP地址&#xff0c;以满足特定的网络需求或保护隐私。本文将为您详细介绍如何改变网络IP地址的方法&#xf…

【C++】 C/C++预处理器介绍

C预处理器&#xff08;Preprocessor&#xff09;是编译过程中的一个阶段&#xff0c;它在编译器进行实际编译之前对源代码进行处理。预处理器提供了一系列的指令&#xff0c;用于条件编译、文件包含、宏定义等操作。以下是一些常见的预处理器指令&#xff1a; 宏定义&#xff…

JL-杰理芯片-认识TA的SDK的第五天

如何区分左右耳我把烧录好了文件,连接手机后,发现对耳变成了两个设备,这个要怎么设置成一个设备? 一个左声道,一个右声道,就能解决 左耳、右耳、单耳耳机无法配对(对耳)如果AD6983D对耳无法配对 <

高通骁龙(Qualcomm Snapdragon)CDSP HVX HTP 芯片简介与开发入门

1. Hexagon DSP/HVX/HTP 硬件演进 说到高通骁龙芯片大家应该不会陌生&#xff0c;其作为最为广泛的移动处理器之一&#xff0c;几乎每一个品牌的智能手机都会使用高通骁龙的处理器。 高通提供了一系列骁龙芯片解决方案。根据性能强弱分为了5个产品系列&#xff1a;从最高端的…

【数学建模】 数据处理与拟合模型

文章目录 数据处理与拟合模型1. 数据与大数据1.1 什么是数&#xff1f;什么是数据&#xff1f;1.2 数据与大数据1.3 数据科学的研究对象 2. 数据的预处理2.1 为什么需要数据预处理2.2 使用pandas处理数据的基础2.3 pandas常用方法总结2.4 数据的规约1) 维度规约2) 数值规约3) 数…

WAF的新选择,雷池 SafeLine-安装动态防护使用指南

什么是 WAF WAF 是 Web Application Firewall 的缩写&#xff0c;也被称为 Web 应用防火墙。 区别于传统防火墙&#xff0c;WAF 工作在应用层&#xff0c;对基于 HTTP/HTTPS 协议的 Web 系统有着更好的防护效果&#xff0c;使其免于受到黑客的攻击&#xff1b; 通俗来讲&#…

推动高效能:东芝TB67H301FTG全桥直流电机驱动IC

在如今高度自动化的时代&#xff0c;电子产品的性能和效率成为了工程师们关注的焦点。东芝的TB67H301FTG全桥直流电机驱动IC应运而生&#xff0c;以其卓越的技术和可靠性&#xff0c;成为众多应用的理想选择。无论是在机器人、家用电器、工业自动化&#xff0c;还是在其他需要精…

小型语言模型SLM:趋势和用例

前言 近年来&#xff0c;GPT 和 BERT 等大型语言模型 (LLM) 不断发展&#xff0c;参数数量从数亿飙升至 GPT-4 等后继者的一万亿以上。然而&#xff0c;不断增长的参数规模引出了一个问题&#xff1a;对于企业应用来说&#xff0c;参数规模越大就一定越好吗&#xff1f; 答案…

Java8 新特性stream、forEach常用方法总结

1、去重 List<Long> list new ArrayList<>();list.add(1L);list.add(2L);list.add(3L);list.add(3L);list.stream().distinct().collect(Collectors.toList()); 2、筛选出符合条件的数据 1&#xff09;单条件筛选 筛选出性别为男的学生&#xff1a; List<…

35 智能指针

目录 为什么需要智能指针&#xff1f;内存泄露智能指针的使用及原理c11和boost中智能指针的关系RAII扩展学习 1. 为什么需要智能指针&#xff1f; 下面我们先分析一下下面这段程序有没有什么内存方面的问题&#xff1f; int div() {int a, b;cin >> a >> b;if (…

Ubuntu无法安全地用该源进行更新,所以默认禁用该源。

解决方案 1. 获取并添加缺失的 GPG 公钥 可以使用 apt-key 命令来添加缺失的公钥。根据错误信息&#xff0c;缺失的公钥是 3B4FE6ACC0B21F32。 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F322. 更新软件包列表 添加公钥后&#xff0c;更…

centos7 修改mysql的datadir流程

旧目录&#xff1a;/var/lib/mysql/ 新目录&#xff1a;/data/mysqlData/ 1、先将 cp my.cnf /etc/my.cnf 然后再yum install mysql-community-server开始安装mysql… (第一次安装的话&#xff0c;一般只执行第一步就可以了) 2、修改datadir的值 3、创建新目录 4、删除旧目录…

分享一款超火的的发型stable diffusion提示词!

前言 1、女性发型 Tag短发侧刘海高马尾麻花辫甜美卷发半扎发侧分卷发半扎马尾发波浪空气刘海波波头高马尾空气刘海自然波浪卷复古波浪卷发短发齐刘海矮扎丸子头露出额头小波浪刘海披肩卷发英文Short Hair with Side BangsHigh Ponytail BraidSweet CurlsHalf-Up HairSide-Part…

用Python轻松转换Markdown文件为PDF文档

Markdown&#xff0c;以其简洁的语法和易于阅读的特性&#xff0c;成为了许多作家、开发者和学生记录思想、编写教程或撰写报告的首选格式。然而&#xff0c;在分享或打印这些文档时&#xff0c;Markdown的纯文本形式可能无法满足对版式和布局的专业需求。而将Markdown转换为PD…

【经验篇】Spring Data JPA开启批量更新时乐观锁失效问题

乐观锁机制 什么是乐观锁&#xff1f; 乐观锁的基本思想是&#xff0c;认为在大多数情况下&#xff0c;数据访问不会导致冲突。因此&#xff0c;乐观锁允许多个事务同时读取和修改相同的数据&#xff0c;而不进行显式的锁定。在提交事务之前&#xff0c;会检查是否有其他事务…

Linux和windows网络配置文件的修改

Linux和windows网络配置文件的修改 网络配置文件是计算机网络管理中至关重要的一部分。正确配置网络文件可以确保计算机与网络设备之间的通信顺畅&#xff0c;避免网络故障。本文将详细介绍网络配置文件的修改方法&#xff0c;包括常见命令、使用方法举例&#xff0c;以及一些…

《雅思口语真经总纲1.0》话题实战训练笔记part1——Work or study(必考)

《雅思口语真经总纲1.0》笔记——第四章&#xff1a;口语素材大全&#xff08;part1、part2、part3回答准则及练习方法&#xff0c;不包括范例答案&#xff09;★★★★★ 文章目录 Work or study&#xff08;必考&#xff09;Are you a student or do you have a job?2024063…