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

embedded/2025/2/2 16:06:37/

一、优先级队列

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。

java"> 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. 将最后新插入的节点向上调整,直到满足的性质

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

java"> 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. 对顶元素进行向下调整

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

完整代码

java">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.ppmy.cn/embedded/158938.html

相关文章

Java面试题2025-设计模式

1.说一下开发中需要遵守的设计原则&#xff1f; 设计模式中主要有六大设计原则&#xff0c;简称为SOLID &#xff0c;是由于各个原则的首字母简称合并的来(两个L算一个,solid 稳定的)&#xff0c;六大设计原则分别如下&#xff1a; 1、单一职责原则 单一职责原则的定义描述非…

upload labs靶场

upload labs靶场 注意:本人关卡后面似乎相比正常的关卡少了一关&#xff0c;所以每次关卡名字都是1才可以和正常关卡在同一关 一.个人信息 个人名称&#xff1a;张嘉玮 二.解题情况 三.解题过程 题目&#xff1a;up load labs靶场 pass 1前后端 思路及解题&#xff1a;…

【Redis】set 和 zset 类型的介绍和常用命令

1. set 1.1 介绍 set 类型和 list 不同的是&#xff0c;存储的元素是无序的&#xff0c;并且元素不允许重复&#xff0c;Redis 除了支持集合内的增删查改操作&#xff0c;还支持多个集合取交集&#xff0c;并集&#xff0c;差集 1.2 常用命令 命令 介绍 时间复杂度 sadd …

多模态论文笔记——VDT

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本文详细解读多模态论文《VDT》&#xff0c;首次在视频扩散的生成模型中使用Transformer&#xff0c;这和后面的Sora架构最接近。 文章目录 论文摘要1 引言近期研究…

Luzmo 专为SaaS公司设计的嵌入式数据分析平台

Luzmo 是一款嵌入式数据分析平台&#xff0c;专为 SaaS 公司设计&#xff0c;旨在通过直观的可视化和快速开发流程简化数据驱动决策。以下是关于 Luzmo 的详细介绍&#xff1a; 1. 背景与定位 Luzmo 前身为 Cumul.io &#xff0c;专注于为 SaaS 公司提供嵌入式分析解决方案。…

【llm对话系统】大模型 Llama 源码分析之 Flash Attention

1. 写在前面 近年来&#xff0c;基于 Transformer 架构的大型语言模型 (LLM) 在自然语言处理 (NLP) 领域取得了巨大的成功。Transformer 的核心组件是自注意力 (Self-Attention) 机制&#xff0c;它允许模型捕捉输入序列中不同位置之间的关系。然而&#xff0c;标准的自注意力…

DIY QMK量子键盘

最近放假了&#xff0c;趁这个空余在做一个分支项目&#xff0c;一款机械键盘&#xff0c;量子键盘取自固件名称QMK&#xff08;Quantum Mechanical Keyboard&#xff09;。 键盘作为计算机或其他电子设备的重要输入设备之一&#xff0c;通过将按键的物理动作转换为数字信号&am…

2024-2025自动驾驶技术演进与产业破局的深度实践——一名自动驾驶算法工程师的年度技术总结与行业洞察

一、引言&#xff1a;站在自动驾驶的"技术奇点" 2024年是自动驾驶行业从"技术验证"迈向"商业化落地"的关键转折点。从特斯拉FSD V12的端到端技术突破&#xff0c;到中国L3法规的破冰&#xff0c;从大模型重构感知架构&#xff0c;到城市NOA的&qu…