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

news2024/12/29 10:19:16

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

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

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

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

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

在这里插入图片描述

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

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

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

前言

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

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

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

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

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

目录

  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.coloradmin.cn/o/1890925.html

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

相关文章

取证与数据恢复:冷系统分析,实时系统分析与镜像分析之间的过渡办法

天津鸿萌科贸发展有限公司是 ElcomSoft 系列取证软件的授权代理商。 ElcomSoft 系列取证软件 ElcomSoft 系列取证软件支持从计算机和移动设备进行数据提取、解锁文档、解密压缩文件、破解加密容器、查看和分析证据。 计算机和手机取证的完整集合硬件加速解密最多支持10,000计…

arduino IDE 处于read only editor模式

当我们浏览一些arduino的例子的时候&#xff0c;有时候想修改这些例子。但是这些例子即使另存到自己的文件目录下&#xff0c;仍然不能修改&#xff0c;提示处于read only 模式。 网上有一些什么说法&#xff0c;说要设置什么之类的&#xff0c;当我们点开之后&#xff0c;好像…

13-4 GPT-5:博士级AI,人工智能的新时代

图片来源&#xff1a;AI Disruptive 人工智能世界正在迅速发展&#xff0c;新的创新和突破层出不穷。在本文中&#xff0c;我们将深入探讨最新的进展&#xff0c;从即将推出的 GPT-5 模型到 Apple 和 Meta 之间可能的合作。 GPT-5&#xff1a;博士级别的人工智能 虽然尚未正…

Windows Server 2008近源应急OS-1

前景需要&#xff1a;小王从某安全大厂被优化掉后&#xff0c;来到了某私立小学当起了计算机老师。某一天上课的时候&#xff0c;发现鼠标在自己动弹&#xff0c;又发现除了某台电脑&#xff0c;其他电脑连不上网络。感觉肯定有学生捣乱&#xff0c;于是开启了应急。 我们需要…

微信小程序 typescript 开发日历界面

1.界面代码 <view class"o-calendar"><view class"o-calendar-container" ><view class"o-calendar-titlebar"><view class"o-left_arrow" bind:tap"prevMonth">《</view>{{year}}年{{month…

py黑帽子学习笔记_burp

配置burp kali虚机默认装好了社区版burp和java&#xff0c;其他os需要手动装 burp是用java&#xff0c;还得下载一个jython包&#xff0c;供burp用 配apt国内源&#xff0c;然后apt install jython --download-only&#xff0c;会只下载包而不安装&#xff0c;下载的目录搜一…

基于最大相邻夹角的边缘点提取(matlab)

1、背景介绍 边缘点是指点云数据中代表物体或场景几何形状突变的那些点。在三维点云中&#xff0c;边缘点通常标志着不同表面或物体的分界&#xff0c;或者是物体表面上的不规则性&#xff0c;如裂缝、棱角、突起等。点云边缘检测的作用非常重要&#xff0c;最常见是进行特征点…

Transformation(转换)开发-switch/case组件

一、switch/case组件-条件判断 体育老师要做一件非常重要的事情&#xff1a;判断学生是男孩还是女孩、或者是蜘蛛&#xff0c;然后让他们各自到指定的队伍中 体育老师做的事情&#xff0c;我们同样也会在Kettle中会经常用来。在Kettle中&#xff0c;switch/case组件可以来做类似…

私有云统一多云管理平台主要服务内容

私有云统一多云管理平台&#xff0c;作为企业IT架构现代化的关键组成部分&#xff0c;旨在为企业提供高效、灵活、安全的云计算资源管理解决方案。这类平台通过整合和优化不同云环境(包括私有云、公有云、混合云)的管理&#xff0c;帮助企业打破云孤岛&#xff0c;实现资源的统…

守护创新之魂:源代码防泄漏的终极策略

在信息化快速发展的今天&#xff0c;企业的核心机密数据&#xff0c;尤其是源代码&#xff0c;成为了企业竞争力的关键所在。然而&#xff0c;源代码的泄露风险也随之增加&#xff0c;给企业的安全和发展带来了巨大威胁。在这样的背景下&#xff0c;SDC沙盒作为一种创新的源代码…

【JAVA入门】Day12 - 权限修饰符

【JAVA入门】Day12 - 权限修饰符 文章目录 【JAVA入门】Day12 - 权限修饰符一、private二、空着不写三、protected四、public五、权限修饰符的使用规则 权限修饰符是用来控制一个成员能够被访问的范围的。 权限修饰符可以修饰成员变量、方法、构造方法、内部类。 publ…

解析Kotlin中的内联函数,inline、noinline、crossinline【笔记摘要】

用编译时常量的概念&#xff0c;引出本文要讲内联函数inline&#xff1a; 1.编译时常量 Java的编译时常量 Compile-time Constant 它有四个要求&#xff1a;1.这个变量需要是 final 的  2.类型只能是字符串或者基本类型  3.这个变量需要在声明的时候就赋值  4.等号右边…

华为路由器静态路由配置(eNSP模拟实验)

实验目标 如图下所示&#xff0c;让PC1ping通PC2 具体操作 配置PC设备ip 先配置PC1的ip、掩码、网关。PC2也做这样的配置 配置路由器ip 配置G0/0/0的ip信息 #进入系统 <Huawei>system-view #进入GigabitEthernet0/0/0接口 [Huawei]int G0/0/0 #设置接口的ip和掩码 […

springboot 自定义的全局捕获异常失效

背景&#xff1a;springbootspringcloud 分布式微服务。 问题&#xff1a;公共模块在使用RestControllerAdvice全局捕获异常时&#xff0c;捕获不到子服务抛出的相应异常 首先看一下全局异常组件有么有被扫描到 如何查看&#xff0c;很简单只需要写一段类加载打印代码&#x…

Ansys Zemax|场曲跟畸变图的前世今生

实现 OpticStudio通过在X和Y方向&#xff08;弧矢和子午方向&#xff09;的傍轴光线追踪确定近轴图像平面的Z坐标&#xff0c;并测量该近轴焦平面与系统图像平面的Z坐标之间的距离。 切向数据是沿Z轴从图像平面到近轴图像平面在切向&#xff08;YZ&#xff09;平面测量的距离…

解决pip安装时的“SyntaxError: invalid syntax”错误

项目场景&#xff1a; 项目中有新的成员加入时&#xff0c;第一步就是安装开发环境&#xff0c;然而往往同样的机器、同样的配置&#xff0c;我们却总能遇到各种各样不同的问题。 今天分享一个简单的操作问题。 问题描述 项目用到pandas&#xff0c;安装pandas时遇到Syntax…

使用 App Store Connect API 生成和读取分析报告

文章目录 前言安装 API Swift SDK配置 API Swift SDK生成分析报告获取所有可用的报告获取报告的分段下载分段的数据总结 前言 Apple 最近推出了50多个新的分析报告&#xff0c;其中包含数百个新的数据点和指标&#xff0c;以帮助开发者了解他们的应用程序的表现情况。 这些报…

k8s部署rancher

一、添加helm chart仓库 二、创建命名空间 # kubectl create ns cattile-system 三、安装cert-manager https://github.com/cert-manager/cert-manager/releases/download/v1.14.5/cert-manager.crds.yaml# kubectl apply -f cert-manager.crds.yaml 四、安装rangcher # he…

day02-广播机制

广播机制 广播是numpy对不同形状的数组进行数值计算的方式&#xff0c;对数组的算术运算通常在相应的元素上进行 1.如果两个数组a和b形状相同&#xff0c;即满足a.shape b.shape&#xff0c;那么a*b的结果就是a与b数组对应位相乘。这要求维数相同且各维度的长度相同 a np.a…

Python成为全球热门语言的“秘密”

1994年&#xff0c;美国举办了一次 针对Python的workshop 从全美国选出来的程序员 聚在一起讨论着这个“秘密武器” Python是如何从一个开发者的“副业” 变成现在全球热门语言呢&#xff1f; 今天我们一起探讨一下Python简史 往下翻看&#xff0c;解锁答案&#x1f447…