Java数据结构 | 模拟实现优先级队列

news2025/7/13 20:38:14

目录

一、前言

二、堆模拟实现优先级队列

2.1 堆的概念

2.2 堆的性质

2.3 堆的存储方式

2.4 堆的创建


一、前言

在前面我们学习过队列,队列是一种先进先出(FIFO)的数据结构,但有些情况下,操作的数据可能带有优先级,一般出队列时,可能需要优先级高的元素先出队列,该中场景下,使用队列显然不合适,比如:在手机上玩游戏的时候,如果有来电,那么系统应该优先处理打进来的电话。

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

二、堆模拟实现优先级队列

JDK1.8中的PriorityQueue底层使用了堆的数据结构,而堆实际就是在完全二叉树的基础之上进行了一些元素的调整。

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 堆的性质

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

  • 堆总是一棵完全二叉树。

与二叉搜索树不同,堆的左右节点都小于根节点,而左右节点的值没有大小关系

2.3 堆的存储方式

堆是一棵完全二叉树,因此可以采用层序的规则来顺序存储

注意:对于非完全二叉树,则不适合使用顺序方式进行存储,因为为了能够还原二叉树空间中必须要存储空节点,就会导致空间利用率比较低

将元素存储到数组中后,可以根据二叉树章节的性质5对树进行还原。假设i为节点在数组中的下标,则有:

  • 如果i为0,则i表示的节点为根节点,否则i节点的双亲节点为 (i - 1)/2

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

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

2.4 堆的创建

  • 堆的向下调整

条件:必须左右子树已经是堆了才能调整

对于集合{ 27,15,19,18,28,34,65,49,25,37 }中的数据,如果将其创建成堆呢?

观察这棵树可以发现,根节点的左右两边已经满足小根堆的性质,只需将根节点进行向下调整即可

调整过程:

  • 将此二叉树的根节点设置为 parent 节点 ,

  • 比较 parent 节点的孩子节点的值,将孩子节点中的较小的节点设置为 child 节点

    • 初始状态

  • 比较 parent 节点和 child 节点的值的大小

    • 若 parent > child , 则不满足小根堆的性质,将两者进行交换。

    • 若 parent < child , 满足小根堆的性质,不进行交换,调整结束。

  • 每次交换后,更新child 和parent的位置, parent =child,child = 2 *parent+1;

  • 代码实现: 时间复杂度: O(logN) — parent固定,child 每次 x 2

//    小根堆的向下调整(满足parent的左子树和右子树已经是堆了)
    public void shiftDown(int parent,int len){
        int child = 2*parent +1;
//        必须保证右左孩子
        while(child < len){
//            找到左右孩子的最小值
            if(child +1 < len && elem[child] > elem[child+1]){
                child++;
            }
            if(elem[child] < elem[parent]){
                int tmp = elem[child];
                elem[child] = elem[parent];
                elem[parent] = tmp;
//                向下调整重新更新
                parent =child;
                child = 2 *parent+1;
            }else{
                break;
            }
        }
    }

针对向下调整的思路,我们可以进行建堆,将一个数组建造成堆,从倒数第一个非叶子节点开始,从后往前遍历数组,依次进行向下调整,就可以得到一个小根堆

例:将以下数组[9,5,2,7,3,6,8] 建成一个小根堆

此时根节点的左右孩子两边的树均满足小根堆的特点,只需要调整以9为根的树向下调整即可,调整过程与结果如下

最后的结果即

  • 代码实现

时间复杂度:建堆的时间复杂度为 O(n) (复杂的数学计算)

    public void crearHeap(){
//        最后一个节点的下标为  i  = usedSize -1
//         (i - 1) / 2 即为最后一个非叶子节点的下标
        for(int parent = (usedSize-1-1)/2; parent >= 0;parent--){
           shiftDown(parent,usedSize);
          //对每一个非叶子节点进行向下调整
        }
    }

usedsize - 即为最后一个叶子节点的下标,((usedsize -1) - 1) / 2 即为最后一个非叶子节点的下标

  • 堆的向上调整

当我们进行元素的插入时,仍要保证这个堆是一个大根堆,则需要对堆进行向上调整

将堆进行向上调整的步骤

  • 将插入的元素即最后一个叶子节点设置为 child ,其父亲节点设置为parent = (child-1) /2

  • 当child > parent 时 , 不满足大根堆的性质,将父亲节点的值与叶子节点的值进行交换

  • 当child <parent 时,满足条件,不需要进行调整

调整完成之后重新更新parent 和 child 的位置,即 child = parent , parent = 2 * child +1

向上调整为大根堆的代码实现如下:

  public void shiftUp(int child){
        int parent = (child-1) /2;
        while(child > 0){
            if(elem[child] > elem[parent]){
                int tmp = elem[child];
                elem[child] = elem[parent];
                elem[parent] = tmp;
                child = parent;
                parent = (child -1)/2;
            }else {
                break;
            }
        }
    }
  • 堆中插入一个元素时,代码实现

public void offer(int val){
//如果堆为满,则对数组进行扩容
        if(isFull()){
            elem = Arrays.copyOf(elem,2*elem.length);
        }
 //将插入的元素设置为堆的最后一个元素
        this.elem[usedSize] = val;
        usedSize++;
 //将堆中元素进行向上调整
        shiftUp(usedSize-1);
    }
     public boolean isFull(){
        return elem.length == usedSize;
    }
  • 堆的删除(删除堆顶元素)

    • 将堆顶元素与队中堆中最后一个节点的值进行交换

    • 将堆中的元素值减少一个

    • 对堆中的元素进行向下调整

代码实现如下:

public int pop(){
        if(isEmpty()){
            return -1;
        }
        int tmp = elem[0];
        elem[0] = elem[usedSize -1];
        elem[usedSize -1] = tmp;
        usedSize--;
    //将堆中元素进行向下调整
        shiftDown(0,usedSize);
        return tmp;
    }
  • 使用堆模拟实现优先级队列

public class TestHeap {
    public int[] elem;
​
    public int usedSize;
​
    public static int DEFAULT_SIZE = 10 ;
​
    public TestHeap() {
        this.elem = new int[DEFAULT_SIZE];
    }
​
    public void init(int[] array){
        for(int i = 0; i < array.length;i++){
            elem[i] = array[i];
            usedSize++;
        }
    }
//   建堆的时间复杂度为O(n)
    public void crearHeap(){
//        最后一个节点的下标为  i  = usedSize -1
//         (i - 1) / 2 即为父亲节点的下标
        for(int parent = (usedSize-1-1)/2; parent >= 0;parent--){
           shiftDown(parent,usedSize);
        }
    }
    /**
     *
     * @param parent 每棵子树的根节点
     * @param len 每棵子树的
     * 时间复杂度:O(log(n))
     */
//    小根堆的向下调整(满足parent的左子树和右子树已经是堆了)
    public void shiftDown(int parent,int len){
        int child = 2*parent +1;
//        必须保证右左孩子
        while(child < len){
//            找到左右孩子的最小值
            if(child +1 < len && elem[child] > elem[child+1]){
                child++;
            }
            if(elem[child] < elem[parent]){
                int tmp = elem[child];
                elem[child] = elem[parent];
                elem[parent] = tmp;
//                向下调整重新更新
                parent =child;
                child = 2 *parent+1;
            }else{
                break;
            }
        }
    }
    public void offer(int val){
        if(isFull()){
            elem = Arrays.copyOf(elem,2*elem.length);
        }
        this.elem[usedSize] = val;
        usedSize++;
        shiftUp(usedSize-1);
    }
//    向上调整
        public void shiftUp(int child){
        int parent = (child-1) /2;
        while(child > 0){
            if(elem[child] > elem[parent]){
                int tmp = elem[child];
                elem[child] = elem[parent];
                elem[parent] = tmp;
                child = parent;
                parent = (child -1)/2;
            }else {
                break;
            }
        }
    }
    public boolean isFull(){
        return elem.length == usedSize;
    }
    public boolean isEmpty(){
        return usedSize == 0;
    }
    public int pop(){
        if(isEmpty()){
            return -1;
        }
        int tmp = elem[0];
        elem[0] = elem[usedSize -1];
        elem[usedSize -1] = tmp;
        usedSize--;
        shiftDown(0,usedSize);
        return tmp;
    }
    public int peek(){
        if(isEmpty()){
            return -1;
        }
        return elem[0];
    }
}

ced485cbb11e458d81a746890b32cf3f.gif

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

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

相关文章

mybatis-plus代码生成工具

mybatis-plus版本升级尝试遇到的问题 若遇到高版本&#xff1a;【全局覆盖已有文件的配置已失效&#xff0c;已迁移到策略配置中】or【覆盖已有文件&#xff08;已迁移到策略配置中&#xff0c;3.5.4版本会删除此方法&#xff09;】这句话&#xff0c;可参考文章中解决办法 参考…

python机器人编程——基于单目视觉、固定场景下的自动泊车(下)

目录一、前言二、主要思路step0 设定一个中间位置step1 掉转马头step2 直线匀速前进step3 调整姿态step4 视觉匹配三、效果四、全篇总结一、前言 本篇来讨论一下在固定场景下&#xff0c;如何仅通过单目视觉&#xff0c;实现差速小车的自动停靠&#xff0c;这种方式实现成本比…

_cpp 位图

文章目录1. 位图概念1.1 给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一个数是否在这40亿个数中。2. 位图的实现2.1 运行结果&#xff1a;3. 位图应用3.1 具体代码封装实现如下3.2 部分结果演示&#xff1a;3.3 应用总结&#x…

力扣(LeetCode)791. 自定义字符串排序(C++)

排序 这道题只关心 orderorderorder 出现的字符&#xff0c;在 sss 中的排序。 sss 中不在 orderorderorder 的字符&#xff0c;在排序后是什么位置&#xff0c;不影响答案。 可以用 sortsortsort 函数&#xff0c;传入我们自定义的排序方式&#xff0c;按照 orderorderorder …

【JavaSE】类和对象 (二) —— 封装、包以及 static 关键字

目录 1. 封装 1.1 封装的概念 1.2 访问限定符 2. 包 2.1 包的概念 2.2 导入包中的类 2.3 自定义包 2.3.1 包的基本规则 2.3.2 创建一个包 2.4 包访问权限——defualt 3. 何为封装 3.1 private 关键字 4. static 成员 4.1 对学生类的进一步思考 4.2 static 修饰成员变量 4.3 …

计算机毕业设计之java+javaweb的物业管理系统

项目介绍 系统权限按管理员,物业和住户这三类涉及用户划分。 (a) 管理员&#xff1a;管理员使用本系统涉到的功能主要有&#xff1a;首页,个人中心,用户管理,员工管理,房屋类型管理,房源信息管理,房屋预约管理,订单信息管理,我的收藏管理,系统管理等功能。 (b) 住户&#xf…

基于单片机的导盲拐杖设计

目 录 引言 1 1 系统概述 1 1.1 设计研究的背景和意义 1 1.2 本次设计内容 1 2 系统设计的整体方案 2 2.1 主控芯片的方案论证 2 2.2 显示模块的方案论证 3 2.3 本章小节 4 3 系统硬件电路设计 4 3.1 单片机最小系统的电路设计 4 3.1.1 STC…

李峋同款爱心代码!跳动的心,给你爱的人一个惊喜!

Hello 大家好 如何浪漫的表白&#xff0c;作为程序员出身的小编&#xff0c;今天就带你实现热播剧《点燃我&#xff0c;温暖你》中超火的李峋同款爱心代码&#xff01;前面是教程&#xff0c;怕麻烦的朋友可以直接划到文末&#xff0c;下载现成的&#xff0c;下载完成后打开就可…

java毕业设计基于的校园头条新闻管理系统的设计与实现(附源码、数据库)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis Maven Vue 等等组成&#xff0c;B/…

【Linux】基础:进程地址空间

【Linux】基础&#xff1a;进程地址空间 摘要&#xff1a;本文首先通过复习关于C语言内存空间的知识来做实验提出问题&#xff0c;从而引入进程的地址空间。需要理解的是进程地址空间的组织形式与其表示意义&#xff0c;在需要理解如何完成进程地址空间的划分以及关键对应物理内…

C++12 ---对象于对象的关系

一、对象于对象的关系 在一个系统中&#xff0c;一个对象可能与不同的对象相关&#xff0c;以下是不同的关系。 依赖(Dependency) (使用一个) 关联(Association) (使用一个) 聚合(Aggregation) (有一个) 组合(Composition ) (有一个&#xff0c;"用..来实现") …

从Matlab实例学习遗传算法

文章目录前言问题背景遗传算法Matlab实例代码附录君主方案遗传算法解决旅行商问题前言 本文旨在使用智能优化算法及其MATLAB实例&#xff08;第2版&#xff09; 一书中的例子&#xff0c;来透彻理解遗传算法的本质。 问题背景 目标&#xff1a; 求解最大化函数 f(x)x10sin⁡…

2023最新SSM计算机毕业设计选题大全(附源码+LW)之java星光之夜香水网站的设计与开发bfmcr

大学计算机专业毕业的&#xff0c;实际上到了毕业的时候&#xff0c;基本属于会与不会之间。说会&#xff0c;是因为学了整套的理论和方法&#xff0c;就是所谓的科班出身。说不会&#xff0c;是因为实践能力极差。 不会的问题&#xff0c;集中体现在毕设的时候&#xff0c;系…

CTFSHOW菜狗杯 web

文章目录web签到web2 c0me_t0_s1gn我的眼里只有$抽老婆一言既出驷马难追TapTapTapWebshell化零为整无一幸免传说之下&#xff08;雾&#xff09;算力超群算力升级2遍地飘零茶歇区小舔田&#xff1f;LSB探姬Is_Not_Obfuscate龙珠NFTweb签到 eval($_REQUEST[$_GET[$_POST[$_COOK…

Ubuntu22.04虚拟机配置双网

文章目录Ubuntu22.04虚拟机配置双网一、 虚拟机网络1、 简介1.1 概述1.2 四种网络2、 配置双网2.1 NAT2.2 主机模式3、 添加到虚拟机二、 ubuntu设置Ubuntu22.04虚拟机配置双网 一、 虚拟机网络 1、 简介 1.1 概述 近期在使用VirtualBox的时候遇到这样的场景&#xff0c;我…

Docker(五)—— 镜像原理、容器快照commit

一、如何得到镜像 1&#xff09;从远程仓库下载 2&#xff09;朋友/同事拷贝给你 3&#xff09;自己制作DockerFile 二、联合文件系统 Docker的镜像是由一层层的文件系统组成&#xff0c;这种层级的文件系统叫做联合文件系统UnionFS。 三、Docker镜像加载原理 1. bootfs:…

第十四届蓝桥杯校内模拟赛第一期——Python

第十四届蓝桥杯校内模拟赛第一期——Python 文章目录第十四届蓝桥杯校内模拟赛第一期——Python1.二进制位数问题描述参考答案扩展2. 晨跑问题描述参考答案扩展3. 调和级数问题描述参考答案4. 山谷问题描述参考答案5. 最小矩阵问题描述参考答案6. 核酸日期问题描述输入格式输出…

redux与react-redux的学习笔记之react-redux

redux与react-redux前言一、redux和react-redux是啥&#xff1f;二、redux使用步骤1.引入库2.原理图原理图释义actions1&#xff09;含义2&#xff09;demostore.js1&#xff09;含义2&#xff09;demoreducer.js1&#xff09;含义2&#xff09;demoCount.jsx1&#xff09;含义…

2022年,我们为什么要学习C++?

“C已死” 大学时代&#xff0c;我就听过这样的说法——差不多十多年前的事儿了。那时候至少在美国&#xff0c;Java已经成了各公司的主流语言。程序员也许都很熟悉Joel Spolsky在2005年12月对JavaSchools发起的批驳。此外&#xff0c;作为微软应对Java的手段&#xff0c;2000…

Ubuntu环境配置(instant-ngp)

综合环境配置 这篇文章的综合配置我是在恒源云上配的&#xff0c;自己穷买不起机子&#xff0c;就只能租咯&#xff0c;这家价格还行&#xff0c;而且可以装VNC&#xff0c;非推广&#xff0c;只是感觉方便&#xff0c;请大家结合自身实际情况 数据上传 这里有几种方法&…