【数据结构】关于优先级队列(堆),你了解内部原理吗?(超详解!!!)

news2024/9/20 8:01:55

前言:

🌟🌟Hello家人们,这期讲解二叉树的遍历,希望你能帮到屏幕前的你。

🌈上期博客在这里:http://t.csdnimg.cn/EdeWV

🌈感兴趣的小伙伴看一看小编主页:GGBondlctrl-CSDN博客

目录

📚️1.优先级队列 

1.1优先级队列的概念

📚️2.优先级队列的模拟(重点)

2.1什么是堆

2.2堆的存储

2.3堆的创建🌟🌟

1.引言

2.思路分析

3.代码实现

2.4堆的插入 🌟🌟

1.思路 

2.代码实现

2.5堆的删除🌟🌟

1.思路

2.代码实现

📚️3.常用接口的介绍

3.1PriorityQueue的特性

3.2PriorityQueue常用接口介绍 

3.3其他函数功能介绍


📚️1.优先级队列 

1.1优先级队列的概念

        前面介绍过队列,队列是一种先进先出(FIFO)的数据结构,但有些情况下,操作的数据可能带有优先级,一般出队列时,可能需要优先级高的元素先出队列,该中场景下,使用队列显然不合适。

比如:在手机上玩游戏的时候,如果有来电,那么系统应该优先处理打进来的电话;初中那会班主任排座位时可能会让成绩好的同学先挑座位

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

📚️2.优先级队列的模拟(重点)

2.1什么是堆

       如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

       总结:总的来说,根结点分为左右两棵树,每棵树都满足根结点大于(小于)其子结点,叫做大根堆(小根堆)

 

2.2堆的存储

从堆的概念可知,堆是一棵完全二叉树,因此可以层序的规则采用顺序的方式来高效存储,

对于完全二叉树有以下性质:

• 如果i为0,则i表示的节点为根节点,否则i节点的双亲节点为 (i - 1)/2
• 如果2 * i + 1 小于节点个数,则节点i的左孩子下标为2 * i + 1,否则没有左孩子

• 如果2 * i + 2 • 小于节点个数,则节点i的右孩子下标为2 * i + 2,否则没有右孩子

这里小编在前几期二叉树讲过博客地址: http://t.csdnimg.cn/bnfaS

2.3堆的创建🌟🌟

1.引言

       对于堆而言,虽然是一个完全二叉树,但是在创建过程中其实并没有用到二叉树的相关知识,其内部原理是顺序表,数组的运用,并且由于要涉及根结点与子结点的大小比较所以要用到上述公式,进行解决。

2.思路分析

图解:

       在每次判断后我们要进行子结点的大小比较,然后进行与根结点的大小比较,然后再进行交换(这里小编将创建大根堆),然后判断交换后的根结点是否满足大根堆条件,此时就要向下再次判断,最终实现大根堆的创建

3.代码实现

1.对的初始化实现创建

public class PriorityQueue {
    public int usedSize=9;
    public int elme[];
    public PriorityQueue(){
        this.elme=new int[usedSize];
    }

    public void creatArray(int array[]){
        for (int i = 0; i <array.length ; i++) {
            elme[i]= array[i];
        }
    }

注解:小编这里为了方便在主函数实现数据替换。

2.实现堆的创建

 public int[] creatQueue(){
        creatArray(elme);
        for (int i = (elme.length-2)/2; i >=0 ; i--) { //对每个根节点进行调整
            downQueue(elme,i);
        }
       return elme;
    }
    //此时进行向下调整
    public void downQueue(int elem[],int parent){ 
        int child=2*parent+1;                          //每个根结点的孩子结点
        while (child<usedSize){                        //循环实现交换
            if(elem[child]<elem[child+1]&&(child+1)< usedSize){
                child++;
            }
            if(elem[parent]<elem[child]){              //孩子结点的交换
                swap(parent,child,elem);
            }
            parent=child;                       //判断交换后的树的每个根结点与子结点
            child=parent*2+1;
        }
    }

//交换方法

private void swap(int parent,int child,int elem[]){
        int temp=elem[parent];
        elem[parent]=elem[child];
        elem[child]=temp;
    }

注解:这里孩子节点等于父亲节点的求法,小编就不再多说了,至于这里的外部循环,就是判断当交换后,需要再次进行二次判断,并且交换,每次交换后,父亲结点等于孩子结点的位置下标,然后再次求其孩子结点,实现向下判断,(那么此时的外部循环就是实现向下循环的条件)

2.4堆的插入 🌟🌟

1.思路 

       首先当堆满的时候要进行扩容,其次当我们插入时,一般在尾部进行插入,然后进行向上调整,又因为,向上调整是向下调整过后的堆,那么就只用看一条树,就是插入尾部的那条树。此时就不需要进行位置是否正确的再次判断。

2.代码实现
public int[] offer(int key){
        if (isFull()){
            this.elme=Arrays.copyOf(elme,elme.length*2);
        }
        elme[usedSize]=key;
        int child=usedSize;
        int parent=(usedSize-1)/2;
        while (true){
            if(elme[parent]<elme[child]){
                swap(parent,child,elme);
            }else {
                break;
            }
            child=parent;
            parent=(parent-1)/2;
        }
        return elme;
    }



 private boolean isFull(){       //判断堆是否满了
        return usedSize== elme.length;
    }

注解:这里小编在尾部插入元素后就进行父子结点与插入结点的判断并交换,然后每次交换,父亲与孩子的节点索引就要进行变化,若当父亲结点大于孩子结点,就直接跳出循环。

2.5堆的删除🌟🌟

1.思路

       这里小编认为删除堆顶元素,就将堆顶元素与末尾元素进行交换,然后有效数据减一,那么就不会对末尾元素进行操作,然后对前面的元素进行向下调整。

2.代码实现
 public int[] poll(){

        swap(0,usedSize-1,elme);
        usedSize--;
        downQueue(elme,0);
        return elme;
    }

📚️3.常用接口的介绍

3.1PriorityQueue的特性

Java集合框架中提供了PriorityQueuePriorityBlockingQueue两种类型的优先级队列PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全

• PriorityQueue中放置的元素必须要能够比较大小,不能插入无法比较大小的对象


• 不能插入null对象,否则会抛出NullPointerException


• 没有容量限制(取决于JVM内存管理,分配机制),可以插入任意多个元素,其内部可以自动扩容


• PriorityQueue底层使用了堆数据结构


• PriorityQueue默认情况下是小堆---即每次获取到的元素都是最小的元素

3.2PriorityQueue常用接口介绍 

PriorityQueue的构造方式:

代码如下:

// 创建一个空的优先级队列,默认容量11
PriorityQueue<Integer> q1 = new PriorityQueue<>();

// 创建一个空的优先级队列,底层的容量为initialCapacity
PriorityQueue<Integer> q2 = new PriorityQueue<>(100);


ArrayList<Integer> list = new ArrayList<>();
list.add(4);
list.add(3);
list.add(2);
list.add(1);
// 用ArrayList对象来构造一个优先级队列的对象
// q3中已经包含了4个元素
PriorityQueue<Integer> q3 = new PriorityQueue<>(list);
System.out.println(q3.size());

 PriorityQueue队列是小堆,如果需要大堆需要用户提供比较器

代码实例:

public class prioritytest {
    public static void main(String[] args) {
        PriorityQueue<Integer> p = new PriorityQueue<>(new IntCmp());
        p.offer(4);
        p.offer(3);
        p.offer(2);
        p.offer(1);
        p.offer(5);
        System.out.println(p.peek());
    }
}
class IntCmp implements Comparator<Integer> { 
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2-o1;
    }
}

输出为5,那么就是完成了大堆的创建。

内部原码如下:

这里的if语句是实现比较的关键,当我们重写compare方法时,如果O2-O1<0那么就说明要进入位置交换实现大根堆,相反那么O1-O2>0那么就不交换,就实现小根堆。

3.3其他函数功能介绍

这里还有clear(),与isEmpty()代表清空与判断是否为空

代码实例:

 int[] arr = {4, 1, 9, 2, 8, 0, 7, 3, 6, 5};
    // 一般在创建优先级队列对象时,如果知道元素个数,建议就直接将底层容量给好
// 否则在插入时需要不多的扩容
// 扩容机制:开辟更大的空间,拷贝元素,这样效率会比较低
    PriorityQueue<Integer> q = new PriorityQueue<>(arr.length);
for(int e:arr){
        q.offer(e);
        }
        System.out.println(q.size()); // 打印优先级队列中有效元素个数
        System.out.println(q.peek()); // 获取优先级最高的元素
// 从优先级队列中删除两个元素之和,再次获取优先级最高的元素
        q.poll();
        q.poll();
        System.out.println(q.size()); // 打印优先级队列中有效元素个数
        System.out.println(q.peek()); // 获取优先级最高的元素
        q.offer(0);
        System.out.println(q.peek()); // 获取优先级最高的元素
// 将优先级队列中的有效元素删除掉,检测其是否为空
        q.clear();
        if(q.isEmpty()){
        System.out.println("优先级队列已经为空!!!")
        }
        else{
        system.out.println("不空")
        }
        

                                       🌅🌅🌅~~~~最后希望与诸君共勉,共同进步!!!

                               💪💪💪以上就是本期内容了, 感兴趣的话,就关注小编吧。

                                                         😊😊  期待你的关注~~~
————————————————     

 

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

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

相关文章

Iinux脚本bash:对自己的应用程序及其相关目录进行备份和恢复,并可查看备份计划、备份状态、备份大小等

目录 一、要求 1、需求 2、需求分析 二、脚本 1、总述 2、创建备份脚本 &#xff08;1&#xff09;脚本命名 &#xff08;2&#xff09;脚本内容 3、创建恢复脚本 &#xff08;1&#xff09;脚本命名 &#xff08;2&#xff09;脚本内容 4、设置cron作业 5、监控脚…

mybatis、mybatis-plus自定义插件,实现自定义策略数据脱敏功能

背景 mybatis中四大组件的作用,下面开发的插件拦截器会使用 四大组件Executor、StatementHandler、ParameterHandler、ResultSetHandler 需求 1、根据脱敏规则进行查询数据,显示的时候进行展示脱敏 2、根据脱敏规则进行查询数据,将脱敏后的数据批量更新回数据库,进行脱…

【Python系列】 Python打印99乘法表

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Vue+ElementUI技巧分享:创建一个带有进度显示的文件下载和打包组件

在现代前端开发中&#xff0c;用户体验至关重要&#xff0c;尤其是在处理文件下载时。为用户提供实时的下载进度显示和打包功能&#xff0c;不仅能提升用户体验&#xff0c;还能使应用更具专业性。在本文中&#xff0c;我们将创建一个 Vue 组件&#xff0c;用于显示文件下载进度…

视图变化 - 等比例变换防止视图拉伸

文章目录 使用场景等比变换等高填充等宽填充代码进行比目标宽高计算超出部分处理设置负的 marginclip 裁剪 End参考&#xff1a; 使用场景 在日常开发中&#xff0c;经常会遇到的一个需求是将图片/视频从界面的一个位置&#xff0c;变换到另一个位置。在处理这类问题的时候经常…

基于微信小程序的书籍销售预测系统的设计与实现(论文+源码)_kaic

摘 要 随着信息化社会的进步&#xff0c;我们的生活越来越便利。在网上&#xff0c;我们可以轻松地进行各种交易&#xff0c;其中包括图书交易。可以说&#xff0c;图书交易是网络交易的一个重要方面。本系统以面向对象的方式进行开发&#xff0c;使用MySQL作为主要数据存储…

linux centos stream9图形化操作

初学者对图形化操作比较熟悉,对字符界面、命令行比较陌生。本文讨论一下图形化操作的基本技能。希望初学者掌握后尽快使用字符界面,会执行命令,更会编程。 本案例是基于stream9版本,如版本不同,会有差别,注意操作使用。 一、安装图形化界面 Linux操作系统常用的图形用…

Unity URP无光照下Shadow 制作 <二> 合批处理

闲谈 相信大家在日常工作中发现了一个问题 &#xff0c; urp下虽然可以做到3个Pass 去写我们想要的效果&#xff0c;但是&#xff0c;不能合批&#xff08;不能合批&#xff0c;那不是我们CPU要干冒烟~&#xff01;&#xff09; 好家伙&#xff0c;熊猫老师的偏方来了 &#x…

Leetcode JAVA刷刷站(38)外观数列

一、题目概述 二、思路方向 为了解决这个问题&#xff0c;我们可以编写一个Java函数countAndSay&#xff0c;该函数接受一个整数n作为输入&#xff0c;并返回外观数列的第n个元素。这个函数将基于递归公式来构建数列&#xff0c;其中countAndSay(1) "1"&#xff0c;…

vue设置环境变量

1、在根目录地下建立两个文件&#xff0c;.env.development 和 .env.production VUE_APP_BASE_API"" .env.development这个就是开发环境&#xff0c;.env.production这个就是生产环境&#xff0c;也就是这个可以写本地的ip端口 .env.production 这个就是生产环境&…

Verilog刷题笔记51

题目&#xff1a; Now that you have a state machine that will identify three-byte messages in a PS/2 byte stream, add a datapath that will also output the 24-bit (3 byte) message whenever a packet is received (out_bytes[23:16] is the first byte, out_bytes[1…

14、springboot3 vue3开发平台-前端-自定义菜单组件,根据路由动态渲染

文章目录 1. 组件2 . 使用示例 1. 组件 src\components\menuTree\index.vue <template><template v-for"item in menuList"><!-- 分为两种方式渲染&#xff1a;有子菜单和没有子菜单--><!-- 没有子菜单--><el-menu-item :index&qu…

linux cpu问题排查及性能优化

cpu性能 一、cpu啥时候才叫有瓶颈 cpu运行的快还是慢、cpu有没有问题&#xff0c;cpu是不是还需要优化。这些是平常开发和运维中经常遇到的问题。那么我门到底如何去判断机器cpu运行的到底有没有异常呢。 从我排查问题来说&#xff0c;单看系统指标不能完全反应应用运行的状态…

移动魔百盒刷机教程

准备工作 确认型号&#xff1a;首先确认您的魔百盒的具体型号&#xff0c;不同的型号可能需要不同的刷机包。刷机包&#xff1a;下载适合您型号的刷机包。U盘&#xff1a;准备一个容量不超过8GB的U盘&#xff0c;并将其格式化为FAT32格式。刷机工具&#xff1a;根据型号可能需…

深度学习实战:手把手教你构建多任务、多标签模型

多任务多标签模型是现代机器学习中的基础架构&#xff0c;这个任务在概念上很简单 -训练一个模型同时预测多个任务的多个输出。 在本文中,我们将基于流行的 MovieLens 数据集,使用稀疏特征来创建一个多任务多标签模型,并逐步介绍整个过程。所以本文将涵盖数据准备、模型构建、训…

keepalived讲解及练习

目录 1、keepalived介绍 1.1 keepalived简介 2、高可用集群 2.1 集群类型 2.2 系统可用性 2.3 系统故障 2.4 实现高可用 3、VRRP 3.1 VRRP&#xff1a;Virtual Router Redundancy Protocol 3.2 VRRP 相关术语 3.3 VRRP相关技术 4、 keepalived实验 4.1 全局配置 4…

Vue封装axios请求(超详细)

一、简介 Vue封装axios请求是指将axios库集成到Vue项目中&#xff0c;以便更方便地发送HTTP请求。首先&#xff0c;需要安装axios库&#xff0c;然后在Vue项目中创建一个名为request.js的文件&#xff0c;用于封装axios实例。在这个文件中&#xff0c;可以设置默认的配置&#…

fastzdp_sqlmodel框架是如何实现更新和删除相关的功能封装的,20240817,Python的国产新ORM框架

根据模型对象更新 初步封装的方法 def update(engine, model_obj, update_dict):"""修改数据:param engine: 连接数据库的引擎对象:param model_obj: 模型对象:param update_dict: 更新字典:return:"""with Session(engine) as session:if not…

Git工具详细使用教程

Git工具详细使用教程 Git是一个分布式版本控制系统&#xff0c;它可以帮助你管理代码的历史记录。本教程将介绍如何使用Git工具进行基本的版本控制操作。 1. 安装Git 首先&#xff0c;你需要在你的计算机上安装Git。你可以从Git官方网站&#xff08;https://git-scm.com/&am…

MySQL(三)——DCL

文章目录 DCL用户管理查询用户创建用户修改用户密码删除用户 权限控制查询权限授予权限撤销权限 DCL DCL&#xff08;Data Control Language&#xff0c;数据控制语言&#xff09;是SQL的一个子集&#xff0c;专门用于定义数据库、表、视图等的访问权限和安全级别。 它允许数据…