数据结构(Java):优先级队列(堆)堆的模拟实现

news2025/1/10 1:31:36

目录

1、优先级队列

1.1 概念

1.2 PriorityQueue底层结构

2、 堆

2.1 堆的概念

 2.2 堆的存储结构

3、优先级队列(堆)的模拟实现

3.1 堆的创建

3.1.1 向下调整算法建完整堆

3.2 堆的插入 

3.2.1 向上调整算法

 3.3 堆的删除

3.4 堆排序


1、优先级队列

1.1 概念

对于队列而言,数据只能从队尾进,队头出,遵循着固定的先进先出原则。

而在某些特殊场景需求下,要求优先级高的元素先出队列,

在这种情况下,数据结构应该提供两个最基本的操作:

  • ①:不仅能添加新对象
  • ②:还能返回最高优先级对象。

这种数据结构就是优先级队列,Java也提供了PriorityQueue集合。 

1.2 PriorityQueue底层结构

PriorityQueue底层是堆这种数据结构,而堆实际就是在完全二叉树的基础上进行了一些调整,接下来,让我们来聊一聊堆到底是什么。


2、 堆

2.1 堆的概念

 所有元素按完全二叉树的顺序存储方式存储在一个一维数组中。

堆分为 大根堆与小根堆。

大根堆:每个节点都大于或等于其子节点。

小根堆:每个节点都小于或等于其子节点。


 2.2 堆的存储结构

我们已知堆为一棵完全二叉树,故采用顺序的方式存储在数组中。

而对于我们之前所学的二叉树,为什么没有采用顺序存储而是采用链式存储呢?

因为二叉树并非都为完全二叉树,若采用顺序存储会造成空间浪费:

因为堆采用顺序的方式进行存储,且为完全二叉树,故具有以下性质:

  1. 孩子节点下标为i,则其父亲节点下标为(i - 1)/2
  2. 父亲下标为i,则其左孩子下标为2i+1(2i+1<节点总数的情况下,否则无左孩子)
  3. 父亲下标为i,则其右孩子下标为2i+2(2i+2<节点总数的情况下,否则无右孩子)

3、优先级队列(堆)的模拟实现

注:下文中代码实现的为大根堆

3.1 堆的创建

 向下调整算法

向下调整算法:选出其左右孩子中值较小值元素(建大堆就选较大元素,建小堆就选较小元素,这里以建小堆为例),将这个元素和根节点进行比较,若比根节点还小,就和根节点交换,交换后可能导致子树不满足堆的性质,因此需要继续向下调整。

注意:向下调整算法的使用,必须要求其左右子树必须为大根堆或者小根堆!!!

向下调整算法的时间复杂度为:O(logN),因为最坏情况是从根一路比较到叶子,比较的次数即为完全二叉树的高度次。

3.1.1 向下调整算法建完整堆

我们给出一组数据:{ 27,15,19,18,28,34,65,49,25,37 },要想将这组数据建成堆,我们可以使用向下调整法建堆。

而一组乱序数据其左右子树是不为堆的,那就需要通过向下调整算法从倒数第一个非叶子节点(下标为(数组.length-1-1)/2 )开始建堆,直到将整棵树建成堆。

建堆的时间复杂度为:O(N)!!!,

什么不是O(N*logN)呢?这里给出解释:

向下调整整体建堆代码:

/**
     * 建堆整体时间复杂度:O(N)
     */
    public void createHeap() {
        //从倒数第一个飞非叶子节点开始建堆
        int parent = (this.usedSize - 1 - 1)/2;
        while (parent >= 0) {//O(N)
            siftDown(parent,this.usedSize);
            parent--;
        }
    }
    /**
     *向下调整算法
     * 时间复杂度:O(logN)
     * @param parent 向下调整的起始位置,即根节点
     * @param usedSize 标定调整的结束 若算出的孩子节点坐标>=usedSize时,说明已调整完成
     */
    private void siftDown(int parent, int usedSize) {
        int child = parent*2+1;
        //当没有孩子节点时,说明向下调整完成
        while (child < usedSize) {
            //当右孩子存在时,选出左右孩子的最大值(建大堆)
            if (child + 1 < usedSize) {
                if (elem[child] < elem[child + 1]) {
                    child = child + 1;
                }
            }
            //和根节点进行比较
            if (elem[child] > elem[parent]) {
                swap(elem, parent, child);
                parent = child;
                child = parent*2+1;
            }else {
                //若根节点大,说明已是大堆,break结束
                break;
            }
        }
    }

3.2 堆的插入 

插入数据总共有两个步骤:

  1. 将新元素插入堆的末尾(插入后不再为堆)
  2. 使用向上调整算法调整为堆 

3.2.1 向上调整算法

(这里以建小堆为例):将元素插入到堆的末尾后,和根节点进行比较,如果比根节点还小就和根节点换,继续向上调整,直至满足堆的性质。如果没有根节点小,说明此时已满足堆的性质,调整完成。

时间复杂度:O(logN)

插入元素代码:

/**
     * 插入元素
     * 时间复杂度:O(logN)
     * @param x:新插入元素的值
     */
    public void offer(int x) {
        if (isFull()) {
            grow();
        }
        elem[usedSize] = x;
        siftUp(usedSize);
        usedSize++;
    }

    /**
     * 向下调整算法
     * @param childIndex:新插入元素的下标
     */
    public void siftUp(int childIndex) {
        //找到新节点的父节点的下标
        int parent = (childIndex - 1)/2;
        //parent为负数时,说明调整完成(最坏)
        while (parent >= 0) {
            if (elem[parent] < elem[childIndex]) {
                swap(elem,parent,childIndex);
                childIndex = parent;
                parent = (childIndex - 1)/2;
            }else {
                break;
            }
        }
    }
    /**
     * 如果堆底层的数组满了,就扩容
     */
    private void grow() {
        this.elem = Arrays.copyOf(elem,elem.length*2);
    }

向上调整算法建堆【拓展】 

故我们也可以一个一个的插入元素(使用向上调整算法)来建堆,但是不推荐,因为时间复杂度比向下调整算法建堆要大,为:O(N*logN)

其实主要原因就是:最后一层的元素是最多的,都要一个个向上调整


 3.3 堆的删除

堆的删除一定删除的是堆顶元素。

故,删除元素的思想很简单,即:

  1.  将堆顶元素和堆中最后一个元素交换
  2.  将堆中有效数据个数(usedSize)减一
  3.  对堆顶元素进行向下调整(只需要调整0下标就可以了)

 堆删除代码:

/**
     * 删除堆元素
     */
    public void poll() {
        if (isEmpty()) {
            //如果堆为空,返回
            return;
        }
        //交换堆顶和最后一个元素
        swap(elem,0,usedSize-1);
        //堆元素有效个数-1
        usedSize--;
        //只向下调整0下标即可
        siftDown(0,usedSize);
    }

3.4 堆排序

若要升序排列,要建大根堆;若要降序排列,就要建小根堆。

以排升序为例:若要排升序,则为大堆,排序过程如下:

  1. 将堆顶元素和堆末元素交换,有效数据个数减一(因为堆顶元素为最大值元素,此时最大值元素已来到数组末尾)
  2. 将0下标处向下调整,重新调整为大堆
  3. 继续将堆顶元素和堆末元素交换,有效数据个数减一(堆顶元素为次大值元素,此时次大值元素已来到数组末尾倒数二个位置处)
  4. 将0下标处向下调整,重新调整为大堆
  5. 重复以上过程,直至只剩一个元素的时候,此时数组已有序且为升序排列

堆排序 排升序代码 :

/**
     * 堆排序
     */
    public void heapSort() {
        if (isEmpty()) {
            return;
        }
        int end = usedSize-1;
        while (end > 0) {
            swap(elem,0,end);
            siftDown(0,end);
            end--;
        }
    }

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

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

相关文章

c语言题目之打印单身狗

文章目录 一、题目二、思路三、代码实现 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、题目 二、思路 第一步 首先这里先了解两个有关于位操作符异或的知识点 &#xff0c;异或操作符的规则是相同为0&#xff0c;相异为1 。 通过上面我们可以得…

AIGC前沿 | LivePortrait

0. 资源链接 论文超链接: LivePortrait 项目: https://github.com/KwaiVGI/LivePortrait 1. 背景动机 现有AIGC存在的问题 随着智能手机和其他录制设备的普及&#xff0c;人们越来越频繁地捕捉静态肖像来记录珍贵而美好的时刻&#xff0c;但这些静态图像缺乏动态表现力和实…

docker默认存储地址 var/lib/docker 满了,换个存储地址操作流程

1. 查看docker 存储地址 docker info如下 var/lib/docker2、查看内存大小 按需执行 df -h 找超过100M的大文件 find / -type f -size 100M -exec ls -lh {} \; df -Th /var/lib/docker 查找这个文件的容量 df -h 查找所有挂载点 du -hs /home/syy_temp/*1、df -h 2、sud…

数据结构(单链表算法题)

1.删除链表中等于给定值 val 的所有节点。 OJ链接 typedef struct ListNode ListNode;struct ListNode {int val;struct ListNode* next; };struct ListNode* removeElements(struct ListNode* head, int val) {//创建新链表ListNode* newhead, *newtail;newhead newtail N…

视频联网共享平台LntonCVS视频监控汇聚平台视频云解决方案

LntonCVS流媒体平台是一款遵循国家GB28181标准协议的先进视频监控与云服务平台。该平台设计独特&#xff0c;能够同时接入并处理多路设备的视频流&#xff0c;支持包括RTSP、RTMP、FLV、HLS、WebRTC在内的多种视频流格式的分发。其功能丰富多样&#xff0c;涵盖了视频直播监控、…

如何在Mac下修改VSCode侧边栏字体大小

在日常使用VSCode&#xff08;Visual Studio Code&#xff09;进行开发时&#xff0c;我们有时需要对IDE&#xff08;集成开发环境&#xff09;的界面进行一些个性化的调整&#xff0c;以提升我们的开发体验。 比如&#xff0c;有些用户可能会觉得VSCode的侧边栏字体大小不符…

国产麒麟、UOS在线打开pdf加盖印章

PageOffice支持两种电子印章方案&#xff0c;可实现对Word、Excel、PDF文档加盖PageOffice自带印章或ZoomSeal电子印章&#xff08;全方位保护、防篡改、防伪造&#xff09;。Word和Excel的盖章功能请参考&#xff1a;Word和Excel加盖印章和签字功能 &#xff08;目前只支持win…

FastAPI 学习之路(五十九)封装统一的json返回处理工具

在本篇文章之前的接口&#xff0c;我们每个接口异常返回的数据格式都不一样&#xff0c;处理起来也没有那么方便&#xff0c;因此我们可以封装一个统一的json。 from fastapi import status from fastapi.responses import JSONResponse, Response from typing import Unionde…

[C/C++入门][进制原理]27、计算机种的进制

各种信息进入计算机&#xff0c;都要转换成“0”和“1”的二进制形式。 计算机 采用二进制的原因是&#xff1a; 物理上容易实现&#xff0c;可靠性高。&#xff08;电子元件的通电和不通电就可以表示1和0&#xff0c;所以非常方便&#xff09;运算简单&#xff0c;通用性强。…

【Git远程操作】理解分布式管理 | 创建远程仓库

目录 1.理解分布式管理 多人协作开发 2.创建远程仓库 2.1仓库名&路径 2.2初始化仓库&设置模板 1.理解分布式管理 目前我们学习的所有内容都是在本地来完成的。&#xff08;add /commit /版本撤销回退/分支管理&#xff09; Git是一个分布式 的版本控制系统。 分支…

最新开源的解析效果非常好的PDF解析工具MinerU (pdf2md pdf2json)

毫不夸张的说 PDF解析工具MinerU是照进RAG黑暗中的一道光——这是我对它的评价。我测过太多了文档解析工具&#xff01; 最近在做文档解析的工作。看了很多的开源的文档解析的工具&#xff0c;版面分析的工具&#xff0c;其中包括paddelpaddel这样30kstar的明星工具。但是效果都…

Android SurfaceView 组件介绍,挖洞原理详解

文章目录 组件介绍基本概念关键特性使用场景 SurfaceHolder介绍主要功能使用示例 SurfaceView 挖洞原理工作机制 使用SurfaceView展示图片示例创建一个自定义的 SurfaceView类在 Activity 中使用 ImageSurfaceView注意事项效果展示 组件介绍 在 Android 开发中&#xff0c;Sur…

【20】读感 - 架构整洁之道(二)

概述 继上一篇文章讲了前两章的读感&#xff0c;已经归纳总结的重点&#xff0c;这章会继续跟进的看一下&#xff0c;深挖架构整洁之道。 编程范式 编程范式从早期到至今&#xff0c;提过哪些编程范式&#xff0c;结构化编程&#xff0c;面向对象编程&#xff0c;函数式编程…

前端学习(二)之HTML

一、HTML文件结构 <!DOCTYPE html> <!-- 告诉浏览器&#xff0c;这是一个HTML文件 --><html lang"en"> <!-- 根元素&#xff08;起始点&#xff0c;最外层容器&#xff09; --><head> <!-- 文档的头部&#xff08;元信息&#xff…

spring ioc的原理

1、控制反转(IOC):对象的创建控制权由程序自身转移到外部&#xff08;容器&#xff09; 2、依赖注入(DI):所谓依赖注入&#xff0c;就是由IOC容器在运行期间&#xff0c;动态地将某种依赖关系注入到对象之中。 Spring 中的 IoC 的实现原理就是工厂模式加反射机制。 参考资料…

C++:类和对象 III(初始化列表、explicit、友元、匿名对象)

目录 初始化列表 初始化列表的特点 类型转换、explicit 隐式类型转换 explicit关键字 static成员 静态成员变量 静态成员函数 友元 友元函数 友元类 内部类 匿名对象 编译器优化 初始化列表 初始化列表就是类成员初始化的地方 函数有它声明和定义的地方&#x…

【人工智能】在未来智慧城市的建设及应用分析

作者主页: 知孤云出岫 目录 作者主页:案例分析&#xff1a;人工智能在未来智慧城市的建设及其影响和应用引言一、人工智能在智慧城市中的关键应用领域 案例分析&#xff1a;人工智能在未来智慧城市的建设及其影响和应用 引言 智慧城市是利用信息和通信技术&#xff08;ICT&am…

【开源 Mac 工具推荐之 2】洛雪音乐(lx-music-desktop):免费良心的音乐平台

旧版文章&#xff1a;【macOS免费软件推荐】第6期&#xff1a;洛雪音乐 Note&#xff1a;本文在旧版文章的基础上&#xff0c;新更新展示了一些洛雪音乐的新功能&#xff0c;并且描述更为详细。 简介 洛雪音乐&#xff08;GitHub 名&#xff1a;lx-music-desktop &#xff09;…

政安晨【零基础玩转各类开源AI项目】基于Ubuntu系统部署Hallo :针对肖像图像动画的分层音频驱动视觉合成

政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: 零基础玩转各类开源AI项目 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff01; 本文目标&#xff1a;在Ubuntu系统上部署Hallo&#x…

Linux——Shell脚本和Nginx反向代理服务器

1. Linux中的shell脚本【了解】 1.1 什么是shell Shell是一个用C语言编写的程序&#xff0c;它是用户使用Linux的桥梁 Shell 既是一种命令语言&#xff0c;有是一种程序设计语言 Shell是指一种应用程序&#xff0c;这个应用程序提供了一个界面&#xff0c;用户通过这个界面访问…