数据结构:优先级队列—堆

一、优先级队列

1、优先级队列概念

优先级队列,听名字我们就知道他是一种队列,队列在前面我们已经学习过了,它是一种先进先出的数据结构,但是在特殊的情况下,我们我们队列中元素是带有一定优先级的它需要比我们此时的队头元素,更先的出队列,或者更先的入队列

比如,当我们刚进入游戏的时候,突然有人来敲门,在这种情况下,我们是不是应该先去开门,虽然我们是先进的游戏,但是我们应该先去开门。

或者,当我们要乘坐飞机时,我们是经济舱的乘客,现在我们正在排队检票,下一个就是你,但是这时来了个头等舱的人,他肯定是要比我们先进的。

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

2、堆的概念

我们在前面了解了完全二叉树的概念,每一层的节点都是从左往右的,依次排列,中间不能空着元素。这就是完全二叉树的概念,那么完全二叉树跟我们今天要讲的堆又有什么关系呢?

其实,堆是一个(特殊的)完全二叉树,每个父节点都不大于或者不小于自己的孩子节点,因此我们将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆

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

这就是堆的存储。因此从逻辑上来说,堆是一棵完全二叉树,从存储底层来说,堆底层是一个数组。

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

1、堆的存储方式

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

但是我们要注意,如果此时我们不是堆,而是一棵非完全二叉树,则不适合使用顺序方式进行存储,因为为了能够还原二叉树,空间中必须要存储空节点,就会导致空间利用率比较低

2、堆的创建

根据上述的概念知道,堆有两种:大根堆和小根堆,但如果此时我们给出一个大小顺序不同的集合,我们该如何创建一个大根堆或小根堆呢?

这就要引出我们的一种调整方法:向下调整

(1)堆向下调整

我们以这个集合{ 27,15,19,18,28,34,65,49,25,37}为例,我们先利用层序遍历的方式画出他的二叉树图。

我们希望将这棵二叉树调整成一个大根堆,但根据上图我们发现,他的最后的根节点和左右两边,并不满足一个大根堆的形式,因此我们需要将他进行调整,我们将最后根节点左右两边最大的一个结点与他进行交换,这样我们的最后根节点和他的左右两边就形成了一个大根堆,而这样一个往下调整的过程我们就称它为向下调整,但是我们发现进行调整之后,我们有的之后的根结点和他的左右两边,他就不是一个大根堆了因此我们需对他进行调整,因此在这里我们就得出了我们向下调整的过程

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

2. 如果parent的左孩子存在,即:child< usesize 进行以下操作,直到parent的左孩子不存在如果左孩子存在判断parent右孩子是否存在,存在找到左右孩子中最小的孩子,让child进行标记,然后将parent与较小的孩子child比较,如果:

  (1)parent大于较大的孩子child,调整结束

  (2)否则交换parent与较大的孩子child,交换完成之后,parent中小的元素向下移动,可能导致子树不满足对的性质,因此需要继续向下调整,即parent=child;child=parent*2+1; 然后继续步骤2。

 public void createHeap(){
//我们从下往上走 ,找倒数第⼀个⾮叶⼦节点,从该节点位置开始往前⼀直到根节点,遇到⼀个节点,应⽤向下调整for(int parent = (usesize-1-1)/2;parent >=0 ;parent--){sifDown(parent,usesize);}}public void sifDown(int parent,int usesize){// child先标记parent的左孩⼦,因为parent可能右左没有右int child = 2*parent + 1;while(child < usesize){// 如果右孩⼦存在,找到左右孩⼦中较⼩的孩⼦,⽤child进⾏标记if(child+1 < usesize && elem[child] < elem[child+1]){child++;}// 如果双亲⽐其最⼩的孩⼦还大,说明该结构已经不满⾜堆的特性了,将双亲与较⼩的孩⼦交换if(elem[child] > elem[parent]){swap(child,parent);// parent中⼤的元素往下移动,可能会造成⼦树不满⾜堆的性质,因此需要继续向下调整parent = child;child = 2*parent + 1;}else{break;}}}

(2)堆向上调整:堆的插入

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

1. 先将元素放到堆的最后(注意:空间不够时需要扩容)

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

在这里我们引入了一个新的方法:向上调整,他其实跟我们的向下调整是一样的只是向下调整是传入父亲结点,再去求孩子结点进行判断,而向上调整是传入孩子结点,求父亲结点进行判断

 public void offer(int val){if(isFull()){elem = Arrays.copyOf(elem,2*elem.length);}elem[usesize] = val;sifUp(usesize);usesize++;}public  void sifUp(int child){int parent = (child-1)/2;while(parent >=0){if(elem[child] > elem[parent]){swap(child,parent);child = parent;parent =(child-1)/2;}else{break;}}}

(3)堆的删除

注意:堆的删除我们要删除的是堆顶元素。

堆的删除总共需要三个步骤:

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

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

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

 public  int poll(int val){if(empty()){return -1;}int oldVale = elem[0];swap(0,usesize-1);usesize--;sifDown(0,usesize);return oldVale;}

完整代码

public class TestHeap {public int[] elem;public int usesize;public TestHeap(){elem = new int[10];}public void len(int[] arr){for (int i = 0; i < arr.length; i++) {elem[i] = arr[i];usesize++;}}public void createHeap(){for(int parent = (usesize-1-1)/2;parent >=0 ;parent--){sifDown(parent,usesize);}}public void sifDown(int parent,int usesize){int child = 2*parent + 1;while(child < usesize){if(child+1 < usesize && elem[child] < elem[child+1]){child++;}if(elem[child] > elem[parent]){swap(child,parent);parent = child;child = 2*parent + 1;}else{break;}}}public void swap(int i,int j){int tmp = elem[i];elem[i] = elem[j];elem[j] = tmp;}public void offer(int val){if(isFull()){elem = Arrays.copyOf(elem,2*elem.length);}elem[usesize] = val;sifUp(usesize);usesize++;}public  void sifUp(int child){int parent = (child-1)/2;while(parent >=0){if(elem[child] > elem[parent]){swap(child,parent);child = parent;parent =(child-1)/2;}else{break;}}}public  int poll(int val){if(empty()){return -1;}int oldVale = elem[0];swap(0,usesize-1);usesize--;sifDown(0,usesize);return oldVale;}public boolean isFull(){return elem.length == usesize;}public  boolean empty(){return usesize == 0;}public int peekHeap(){return elem[0];}}

好了今天的分享就到这里了,还请大家多多关注,我们下一篇见!

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

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

相关文章

2025年2月2日(网络编程 tcp)

tcp 循环服务 import socketdef main():# 创建 socket# 绑定tcp_server socket.socket(socket.AF_INET, socket.SOCK_STREAM)tcp_server.bind(("", 8080))# socket 转变为被动tcp_server.listen(128)while True:# 产生专门为链接进来的客户端服务的 socketprint(&qu…

像接口契约文档 这种工件,在需求 分析 设计 工作流里面 属于哪一个工作流

οゞ浪漫心情ゞο(20***328) 2016/2/18 10:26:47 请教一下&#xff0c;像接口契约文档 这种工件&#xff0c;在需求 分析 设计 工作流里面 属于哪一个工作流&#xff1f; 潘加宇(35***47) 17:17:28 你这相当于问用例图、序列图属于哪个工作流&#xff0c;看内容。 如果你的&quo…

Zabbix 推送告警 消息模板 美化(钉钉Webhook机器人、邮件)

目前网络上已经有很多关于Zabbix如何推送告警信息到钉钉机器人、到邮件等文章。 但是在搜索下来&#xff0c;发现缺少了对告警信息的美化的文章。 本文不赘述如何对Zabbix对接钉钉、对接邮件&#xff0c;仅介绍我采用的美化消息模板的内容。 活用AI工具可以减轻很多学习、脑力负…

何谓共赢?

A和B是人或组织&#xff0c;他们怎样的合作才是共赢呢&#xff1f; 形态1:A提供自己的身份证等个人信息&#xff0c;B用来作贷款等一些事务&#xff0c;A每月得到一笔钱。 A的风险远大于收益&#xff0c;或者B从事的是非法行为&#xff1b; 形态2:A单方面提前终止了与B的合作…

物联网 STM32【源代码形式-使用以太网】连接OneNet IOT从云产品开发到底层MQTT实现,APP控制 【保姆级零基础搭建】

物联网&#xff08;IoT&#xff09;‌是指通过各种信息传感器、射频识别技术、全球定位系统、红外感应器等装置与技术&#xff0c;实时采集并连接任何需要监控、连接、互动的物体或过程&#xff0c;实现对物品和过程的智能化感知、识别和管理。物联网的核心功能包括数据采集与监…

Redis|前言

文章目录 什么是 Redis&#xff1f;Redis 主流功能与应用 什么是 Redis&#xff1f; Redis&#xff0c;Remote Dictionary Server&#xff08;远程字典服务器&#xff09;。Redis 是完全开源的&#xff0c;使用 ANSIC 语言编写&#xff0c;遵守 BSD 协议&#xff0c;是一个高性…

架构技能(四):需求分析

需求分析&#xff0c;即分析需求&#xff0c;分析软件用户需要解决的问题。 需求分析的下一环节是软件的整体架构设计&#xff0c;需求是输入&#xff0c;架构是输出&#xff0c;需求决定了架构。 决定架构的是软件的所有需求吗&#xff1f;肯定不是&#xff0c;真正决定架构…

Linux:线程池和单例模式

一、普通线程池 1.1 线程池概念 线程池&#xff1a;一种线程使用模式。线程过多会带来调度开销&#xff0c;进而影响缓存局部性和整体性能。而线程池维护着多个线程&#xff0c;等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价&…

maven mysql jdk nvm node npm 环境安装

安装JDK 1.8 11 环境 maven环境安装 打开网站 下载 下载zip格式 解压 自己创建一个maven库 以后在idea 使用maven时候重新设置一下 这三个地方分别设置 这时候maven才算设置好 nvm 管理 npm nodejs nvm下载 安装 Releases coreybutler/nvm-windows GitHub 一键安装且若有…

【B站保姆级视频教程:Jetson配置YOLOv11环境(六)PyTorchTorchvision安装】

Jetson配置YOLOv11环境&#xff08;6&#xff09;PyTorch&Torchvision安装 文章目录 1. 安装PyTorch1.1安装依赖项1.2 下载torch wheel 安装包1.3 安装 2. 安装torchvisiion2.1 安装依赖2.2 编译安装torchvision2.2.1 Torchvisiion版本选择2.2.2 下载torchvisiion到Downloa…

【算法-位运算】位运算遍历 LogTick 算法

文章目录 1. 引入2. LogTick 优化遍历过程3. 题目3.1 LeetCode3097 或值至少为 K 的最短子数组 II3.2 LeetCode2411 按位或最大的最小子数组长度3.3 LeetCode3209 子数组按位与值为 K 的数目3.4 LeetCode3171 找到按位或最接近 K 的子数组3.5 LeetCode1521 找到最接近目标值的函…

NLP深度学习 DAY5:Sequence-to-sequence 模型详解

Seq2Seq&#xff08;Sequence-to-Sequence&#xff09;模型是一种用于处理输入和输出均为序列任务的深度学习模型。它最初被设计用于机器翻译&#xff0c;但后来广泛应用于其他任务&#xff0c;如文本摘要、对话系统、语音识别、问答系统等。 核心思想 Seq2Seq 模型的目标是将…

吴恩达深度学习——优化神经网络

本文来自https://www.bilibili.com/video/BV1FT4y1E74V&#xff0c;仅为本人学习所用。 文章目录 优化样本大小mini-batch 优化梯度下降法动量梯度下降法指数加权平均概念偏差纠正 动量梯度下降法 RMSpropAdam优化算法 优化学习率局部最优问题&#xff08;了解&#xff09; 优…

Shell篇-字符串处理

目录 1.变量引用 2.获取字符串长度 3.字符串截取 4.删除子字符串 5.字符串替换 总结&#xff1a; Bash&#xff08;Shell 脚本&#xff09;中的字符串处理语法。以下是对其的介绍和总结&#xff1a;Bash 变量可以使用不同的语法来获取、修改和删除字符串的内容。图片中列…

CMake项目编译与开源项目目录结构

Cmake 使用简单方便&#xff0c;可以跨平台构建项目编译环境&#xff0c;尤其比直接写makefile简单&#xff0c;可以通过简单的Cmake生成负责的Makefile文件。 如果没有使用cmake进行编译&#xff0c;需要如下命令&#xff1a;&#xff08;以muduo库echo服务器为例&#xff09;…

书生大模型实战营7

文章目录 L1——基础岛提示词工程实践什么是Prompt(提示词)什么是提示工程提示设计框架CRISPECO-STAR LangGPT结构化提示词LangGPT结构编写技巧构建全局思维链保持上下文语义一致性有机结合其他 Prompt 技巧 常用的提示词模块 浦语提示词工程实践(LangGPT版)自动化生成LangGPT提…

Shadow DOM举例

这东西具有隔离效果&#xff0c;对于一些插件需要append一些div倒是不错的选择 <!DOCTYPE html> <html lang"zh-CN"> <head> <meta charset"utf-8"> <title>演示例子</title> </head> <body> <style&g…

SQLAlchemy 2.0的简单使用教程

SQLAlchemy 2.0相比1.x进行了很大的更新&#xff0c;目前网上的教程不多&#xff0c;以下以链接mysql为例介绍一下基本的使用方法 环境及依赖 Python:3.8 mysql:8.3 Flask:3.0.3 SQLAlchemy:2.0.37 PyMySQL:1.1.1使用步骤 1、创建引擎&#xff0c;链接到mysql engine crea…

《LLM大语言模型+RAG实战+Langchain+ChatGLM-4+Transformer》

文章目录 Langchain的定义Langchain的组成三个核心组件实现整个核心组成部分 为什么要使用LangchainLangchain的底层原理Langchain实战操作LangSmithLangChain调用LLM安装openAI库-国内镜像源代码运行结果小结 使用Langchain的提示模板部署Langchain程序安装langserve代码请求格…

某网盘工具,限速下载上传!

聊一聊 某度盘对于个人和未开通会员的情况下&#xff0c;容量还是有点小。最近自己的盘满了&#xff0c;还有很多东西放不进去。转眼发现正在下载的寻雷&#xff0c;就点进去看看寻雷盘有多大&#xff0c;一看&#xff0c;还好&#xff0c;比某度盘容量大。 但一想&#xff0…