数据结构-优先级队列(堆)

文章目录

目录

文章目录

前言

一 . 堆

二 . 堆的创建(以大根堆为例)

堆的向下调整(重难点)

 堆的创建

 堆的删除

向上调整

堆的插入

三 . 优先级队列

总结


前言

大家好,今天给大家讲解一下堆这个数据结构和它的实现 - 优先级队列


一 . 堆

堆(Heap)是一种基于完全二叉树的数据结构,具有以下特点:

  1. 完全二叉树:堆是一种完全二叉树,即除了最后一层外,其他层的节点都是满的,并且最后一层的节点都靠左排列。

  2. 堆序性:堆中的每个节点都满足堆序性质,即对于最大堆(Max Heap),父节点的值大于或等于其子节点的值;对于最小堆(Min Heap),父节点的值小于或等于其子节点的值。

堆通常用数组来实现,其中数组的索引表示节点在堆中的位置。对于一个节点在索引i的堆,其左子节点在索引2i,右子节点在索引2i+1,父节点在索引i/2。

堆常常被用来实现优先级队列,因为它能够快速找到最大或最小的元素,并且在插入和删除操作时保持堆序性质。

常见的堆有两种类型:

  1. 最大堆(大根堆):父节点的值大于或等于其子节点的值。最大堆的根节点是堆中的最大元素。

  2. 最小堆(小根堆):父节点的值小于或等于其子节点的值。最小堆的根节点是堆中的最小元素。

堆的常见操作包括:

  1. 插入(Insertion):将一个元素插入到堆中,需要保持堆序性质。

  2. 删除根节点(Delete Root):删除堆中的根节点,需要调整堆以保持堆序性质。

  3. 查找最大/最小元素(Find Max/Min):在最大堆中查找最大元素,在最小堆中查找最小元素,时间复杂度为O(1)。

  4. 堆排序(Heap Sort):利用堆的性质进行排序,时间复杂度为O(nlogn)。


二 . 堆的创建(以大根堆为例)

初始化工作

public class BigHeap {int[] elem; // 用来记录堆中的元素int size;public BigHeap(int capacity) {elem = new int[capacity];}//再初始化的时候默认给一个数组public void initHeap(int[] arr) {for (int i = 0; i < arr.length; i++) {elem[i] = arr[i];size++;}}public boolean isFull() {return elem.length == size;}public void swap(int i,int j){int temp = elem[i];elem[i] = elem[j];elem[j] = temp;}

}

堆的向下调整(重难点)

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

父节点的值大于或等于其子节点的值。最大堆的根节点是堆中的最大元素。

根据层序遍历构建出的二叉树显然并不符合我们的要求,这个是时候我们就需要进行向下调整

在最大堆中,向下调整的过程是将当前节点与其子节点中较大的节点进行比较,如果当前节点小于其中较大的子节点,就将它们交换位置。然后,继续向下比较和交换,直到当前节点不再小于其子节点或者已经到达叶子节点。

思考一下,这个时候我们应该从哪个节点进行调整?

我们通常是从最后一个非叶子节点开始向下调整,直到根节点或者到达叶子节点为止。从最后一个非叶子节点开始向下调整的原因是,只有非叶子节点才有子节点,而叶子节点没有子节点,所以没有必要对叶子节点进行向下调整操作。

最后一个非叶子节点的索引可以通过公式计算得到:n/2-1,其中n是堆中元素的数量。

步骤

1. 让parent标记需要调整的节点,child标记parent的左孩子(注意:parent如果有孩子一定先是有左孩子,因为是完全二叉树)

2. 如果parent的左孩子存在,即:child < len, 进行以下操作,直到parent的左孩子不存在

  • parent右孩子是否存在,存在找到左右孩子中最大的孩子,让child进行标记
  • 将parent与较大的孩子child比较如果:
  1. parent小大于较大的孩子child,调整结束
  2. 否则:交换parent与较大的孩子child,交换完成之后,parent中小的元素向下移动,可能导致子树不满足堆的性质,因此需要继续向下调整,即parent = child;child = parent*2+1; 然后继续2(上面的)。

图解

{ 27,15,19,18,28,34,65,49,25,37 }

len: 数组的长度

parent: 表示指向需要调整的节点指针

child: 表示指向孩子节点的指针

最后一个非叶子节点: 根据公式parent = (child-1)/2 在这里child表示最后一个节点的索引

parent = (len - 1 - 1)/2 = 4 我们应该从4索引开始进行向下调整

 进行到这里左子树宣告调整完毕,开始进行右子树的调整

 调整完毕!

代码实现

    private void shiftDown(int parent, int len) {int child = 2 * parent + 1;// 对交换引起的堆结构的改变进行调整(如果改变就调整)while (child < len) {// 找出左右孩子中最大的孩子,用child进行记录if (child + 1 < len && elem[child] < elem[child + 1]) {child++;}// 判断大小关系if (elem[child] > elem[parent]) {swap(child,parent);// parent中大的元素往下移动,可能会造成子树不满足堆的性质,因此需要继续向下调整parent = child;child = 2 * parent + 1;} else {// 左孩子为空,表示以最开始的parent为根的二叉树已经是大根堆结构break;}}}

 堆的创建

    public void createHeap() {// 找倒数第一个非叶子节点,从该节点位置开始往前一直到根节点,遇到一个节点,应用向下调整for (int parent = (size - 1 - 1) / 2; parent >= 0; parent--) {shiftDown(parent, size);}}

 堆的删除

注意:堆的删除一定删除的是堆顶元素。具体如下:

1. 将堆顶元素对堆中最后一个元素交换

2. 将堆中有效数据个数减少一个

3. 对堆顶元素进行向下调整

    public int poll(){int temp = elem[0];swap(0, size);size--;// 调整完之后需要进行先下调整,因为原来的最后一个元素变成了堆顶元素,不用想的肯定不满足大根堆的结构shiftDown(0, size);return temp;}

向上调整

在最大堆中,向上调整的过程是将当前节点与其父节点进行比较,如果当前节点大于其父节点,就将它们交换位置。然后,继续向上比较和交换,直到当前节点不再大于其父节点或者已经到达根节点。

    private void shiftUp(int child) {while (child != 0) {int parent = (child - 1) / 2;if (elem[parent] < elem[child]) {swap(child,parent);child = parent;} else {break;}}}

堆的插入

堆的插入总共需要两个步骤:

1. 先将元素放入到底层空间中(注意:空间不够时需要扩容)

2. 将最后新插入的节点向上调整,直到满足堆的性质

小根堆中插入10

    public void offer(int val) {if (isFull()) {this.elem = Arrays.copyOf(this.elem, 2 * this.elem.length);}elem[size] = val;shiftUp(size);size++;}

 总代码

public class BigHeap {int[] elem;int size;public BigHeap(int capacity) {elem = new int[capacity];}public void initHeap(int[] arr) {for (int i = 0; i < arr.length; i++) {elem[i] = arr[i];size++;}}public void createHeap() {for (int parent = (size - 1 - 1) / 2; parent >= 0; parent--) {shiftDown(parent, size);}}public int poll(){int temp = elem[0];swap(0, size);size--;// 调整完之后需要进行先下调整,因为原来的最后一个元素变成了堆顶元素,不用想的肯定不满足大根堆的结构shiftDown(0, size);return temp;}private void shiftDown(int parent, int len) {int child = 2 * parent + 1;// 对交换引起的堆结构的改变进行调整(如果改变就调整)while (child < len) {// 找出左右孩子中最大的孩子,用child进行记录if (child + 1 < len && elem[child] < elem[child + 1]) {child++;}// 判断大小关系if (elem[child] > elem[parent]) {swap(child,parent);// parent中大的元素往下移动,可能会造成子树不满足堆的性质,因此需要继续向下调整parent = child;child = 2 * parent + 1;} else {// 左孩子为空,表示以最开始的parent为根的二叉树已经是大根堆结构break;}}}public void offer(int val) {if (isFull()) {this.elem = Arrays.copyOf(this.elem, 2 * this.elem.length);}elem[size] = val;shiftUp(size);size++;}private void shiftUp(int child) {while (child != 0) {int parent = (child - 1) / 2;if (elem[parent] < elem[child]) {swap(child,parent);child = parent;} else {break;}}}public boolean isFull() {return elem.length == size;}public void swap(int i,int j){int temp = elem[i];elem[i] = elem[j];elem[j] = temp;}
}

三 . 优先级队列

前面介绍过队列,队列是一种先进先出(FIFO)的数据结构,但有些情况下,操作的数据可能带有优先级,一般出队 列时,可能需要优先级高的元素先出队列,该中场景下,使用队列显然不合适,比如:在手机上玩游戏的时候,如果有来电,那么系统应该优先处理打进来的电话;初中那会班主任排座位时可能会让成绩好的同学先挑座位。 在这种情况下,数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象。这种数 据结构就是优先级队列(Priority Queue)。

优先级队列可以用于很多场景,例如任务调度、进程调度、事件处理等。在任务调度中,可以根据任务的优先级来决定先执行哪些任务;在进程调度中,可以根据进程的优先级来决定先执行哪些进程;在事件处理中,可以根据事件的优先级来决定先处理哪些事件。

在实际应用中,优先级队列可以通过使用堆来实现,因为堆具有良好的时间复杂度和空间复杂度。通过使用堆来实现优先级队列,可以在log₂ n的时间复杂度内插入和删除元素,以及在O(1)的时间复杂度内获取优先级最高的元素。

注意点:

1. 使用时必须导入PriorityQueue所在的包

2. PriorityQueue中放置的元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出 ClassCastException异常

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

4. 没有容量限制,可以插入任意多个元素,其内部可以自动扩容

5. 插入和删除元素的时间复杂度为O(log₂ n)

6. PriorityQueue底层使用了堆数据结构

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

堆模拟实现优先级队列

    class MyPriorityQueue {// 演示作用,不再考虑扩容部分的代码private int[] array = new int[100];private int size = 0;public void offer(int e) {array[size++] = e;shiftUp(size - 1);}public int poll() {int oldValue = array[0];array[0] = array[size--];shiftDown((size-1-1)/2,size);return oldValue;}public int peek() {return array[0];}}


总结

这篇文章给大家重点讲解了堆的模拟实现还有其应用之一 优先级队列,大家好好理解,我们下一篇博客见。

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

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

相关文章

如何使用 Media.io 生成不同年龄的照片

Media.io 是一个在线图片编辑器&#xff0c;提供多种功能&#xff0c;包括照片滤镜、图像裁剪和图像转换。其中&#xff0c;Media.io 的 AI 年龄转换功能可以根据上传的照片&#xff0c;生成不同年龄的照片。 使用 Media.io 生成不同年龄的照片 要使用 Media.io 生成不同年龄…

【word】从正文开始设置页码

在写报告的时候&#xff0c;会要求有封面和目录&#xff0c;各占一页。正文从第3页开始&#xff0c;页码从正文开始设置 word是新建的 分出三节&#xff08;封面、目录、正文&#xff09; 布局--->分割符--->分节符--->下一页 这样就能将word分为3节&#xff0c;分…

Python操作MongoDb创建文档及CRUD基本操作

Python3中类的高级语法及实战 Python3(基础|高级)语法实战(|多线程|多进程|线程池|进程池技术)|多线程安全问题解决方案 Python3数据科学包系列(一):数据分析实战 Python3数据科学包系列(二):数据分析实战 Python3数据科学包系列(三):数据分析实战 MongoDB 操作手册----文档…

1797_GNU pdf阅读器evince

全部学习汇总&#xff1a; GreyZhang/g_GNU: After some years I found that I do need some free air, so dive into GNU again! (github.com) 近段时间经历了很多事情&#xff0c;终于想找一点技术上的自由气氛。或许&#xff0c;没有什么比GNU的一些软件探索更适合填充这样的…

电机-电力拖动-振动-应力分析-设备防护知识初步

1.涉及领域和课程&#xff1a; 信号与系统现代自动化原理与应用频谱转换及振动分析材料学基础与应力分析数学建模、仿真与求解工程数学传感器机器学习与模式识别随机信号处理反馈系统文献学DSP应用机器视觉凸优化&#xff0c;数学物理方法 2.教材推荐 豆瓣书单&#xff0c;更…

如何在终端输出颜色

效果演示: 【看 welcome to here 部分】 环境&#xff1a; Node.js 18.16.0 正文部分 我们可以通过 console.log() 在终端打印字符串。 只要在我们的字符串前面加上转义字符即可。 差不多就是下面这样的结构&#xff1a; 用代码就是&#xff1a; console.log("\x1B…

协议栈——收发数据(拼接网络包,自动重发,滑动窗口机制)

目录 协议栈何时发送数据&#xff5e; 数据长度 IP模块的分片功能 发送频率 网络包序号&#xff5e;利用syn拼接网络包ack确认网络包完整 确定偏移量 服务器ack确定收到数据总长度 序号作用 双端告知各自序号 协议栈自动重发机制 大致流程 ack等待时间如何调整 是…

java做个qq机器人

前置的条件 机器人是基于mirai框架实现的。根据官方的文档&#xff0c;建议使用openjdk11。 我这里使用的编辑工具是idea2023 在idea中新建一个maven项目&#xff0c;虽然可以使用gradle进行构建&#xff0c;不过我这里由于网络问题没有跑通。 pom.xml <dependency>&l…

2023年CSP-J真题详解+分析数据

目录 亲身体验 江苏卷 选择题 阅读程序题 阅读程序(1&#xff09; 判断题 单选题 阅读程序(2) 判断题 单选题 阅读程序(3) 判断题 单选题 完善程序题 完善程序(1) 完善程序(2) 2023CSP-J江苏卷详解 小结 亲身体验 2023年的CSP-J是在9月16日9:30--11:30进行…

竞赛选题 深度学习 opencv python 公式识别(图像识别 机器视觉)

文章目录 0 前言1 课题说明2 效果展示3 具体实现4 关键代码实现5 算法综合效果6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习的数学公式识别算法实现 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学…

【实用工具】谷歌浏览器插件开发指南

谷歌浏览器插件开发指南涉及以下几个方面&#xff1a; 1. 开发环境准备&#xff1a;首先需要安装Chrome浏览器和开发者工具。进入Chrome应用商店&#xff0c;搜索“Extensions Reloader”和“Manifest Viewer”两个插件进行安装&#xff0c;这两个插件可以方便开发和调试。 2…

Win11 安装 Vim

安装包&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1Ru7HhTSotz9mteHug-Yhpw?pwd6666 提取码&#xff1a;6666 双击安装包&#xff0c;一直下一步。 配置环境变量&#xff1a; 先配置系统变量中的path&#xff1a; 接着配置用户变量&#xff1a; 在 cmd 中输入…

图的广度遍历-邻接矩阵实现

description 本题要求实现邻接矩阵存储图的广度优先遍历。 函数接口定义&#xff1a; void BFS(MGraph G,Vertex i); 其中MGraph是邻接矩阵存储的图&#xff0c;定义如下&#xff1a; #define MaxVertexNum 10 /定义最大顶点数/ typedef int Vertex;/* 用顶点下标表示顶点,…

这可能是最全的反爬虫及应对方案,再也不怕爬不到数据了

一、什么是反爬虫 网络爬虫&#xff0c;是一个自动提取网页的程序&#xff0c;它为搜索引擎从万维网上下载网页&#xff0c;是搜索引擎的重要组成。但是当网络爬虫被滥用后&#xff0c;互联网上就出现太多同质的东西&#xff0c;原创得不到保护。于是&#xff0c;很多网站开始…

Java项目:高仿12306

github地址&#xff1a;nageoffer/12306: &#x1f525; 官方推荐 &#x1f525; 大学春招、秋招、应届项目&#xff0c;SpringBoot3 Java17 SpringCloud Alibaba Vue3 等技术架构&#xff0c;完成高仿铁路 12306 用户 抢票 订单 支付服务&#xff0c;帮助学生主打就业的…

网站强制跳转至国家反诈中心该怎么办?怎么处理?如何解封?

在互联网环境中&#xff0c;网站安全是非常重要的。然而&#xff0c;在实际操作过程中&#xff0c;不少网站可能因内容问题、技术安全漏洞等原因被迫下线甚至跳转至国家反诈骗中心网址。面对这一严峻问题&#xff0c;我们如何有效解决&#xff0c;让网站恢复运行并解除强制跳转…

在排序数组中查找元素的第一个和最后一个位置

给你一个按照非递减顺序排列的整数数组 nums&#xff0c;和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target&#xff0c;返回 [-1, -1]。 你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。 示例 1&#xff1a…

解决前端二进制流下载的文件(例如:excel)打不开的问题

1. 现在后端请求数据后&#xff0c;返回了一个二进制的数据&#xff0c;我们要把它下载下来。 这是响应的数据&#xff1a; 2. 这是调用接口的地方&#xff1a; uploadOk(){if(this.files.length 0){return this.$Message.warning("请选择上传文件&#xff01;&#xff…

Multi Label Classification with Missing Labels(MLML)的几种loss设计

多标签学习这个方向问题比较多&#xff0c;可以参考多标签学习的新趋势&#xff08;2021 Survey TPAMI&#xff09; 和 部分标签学习和缺失标签学习到底什么异同&#xff1f; 这两篇偏综述性质的解释。本文重点解释下面几个重点问题&#xff1a; Multi Label Classification w…

第十五章 类和对象——友元

生活中你的家有客厅(Public)&#xff0c;有你的卧室(Private) 客厅所有来的客人都可以进去&#xff0c;但是你的卧室是私有的&#xff0c;也就是说只有你能进去 但是呢&#xff0c;你也可以允许你的好闺蜜好基友进去。 在程序里&#xff0c;有些私有属性 也想让类外特殊的一些…