当前位置: 首页 > news >正文

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

文章目录

目录

文章目录

前言

一 . 堆

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

堆的向下调整(重难点)

 堆的创建

 堆的删除

向上调整

堆的插入

三 . 优先级队列

总结


前言

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


一 . 堆

堆(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];}}


总结

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

相关文章:

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

文章目录 目录 文章目录 前言 一 . 堆 二 . 堆的创建(以大根堆为例) 堆的向下调整(重难点) 堆的创建 堆的删除 向上调整 堆的插入 三 . 优先级队列 总结 前言 大家好,今天给大家讲解一下堆这个数据结构和它的实现 - 优先级队列 一 . 堆 堆&#xff08;Heap&#xff0…...

C++11新特性(语法糖,新容器)

距离C11版本发布已经过去那么多年了&#xff0c;为什么还称为新特性呢&#xff1f;因为笔者前面探讨的内容&#xff0c;除了auto&#xff0c;范围for这些常用的&#xff0c;基本上是用着C98的内容&#xff0c;虽说C11已经发布很多年&#xff0c;却是目前被使用最广泛的版本。因…...

开机可用内存分析Tip

一、开机内存简介 开机内存指的是开机一段时间稳定后的可用内存。一般项目都会挑选同平台其他优秀竞品内存数据&#xff0c;这个也是衡量性能的一个重要标准。所以要进行开机内存检测&#xff0c;同时优化非法内存进程占用。 二、测试前期核查任务 开机内存测试前要进行测试机…...

【Python基础】4. 基本语句

文章目录 注释&#xff08;Comment&#xff09;解释伴随行文本编码问题 输入输出语句&#xff08;Input & Output&#xff09;输出语句普通输出格式化输出&#xff08;3种&#xff09;format 格式总结 输入语句 基本语句if 语句match 语句&#xff08;Python3.10 新增&…...

兼顾友好与安全,隐私协议 Unijoin 助推新一轮 Web3 浪潮

区块链本身不仅崇尚去中心化&#xff0c;同时也崇尚公开透明&#xff0c;虽然这正在让 DAO 治理等变得更加公平&#xff0c;但它同时也是一把双刃剑&#xff0c;个人交易者尤其是一些巨鲸交易者的所以链上交易都被公之于众&#xff0c;这似乎并不是他们想要的结果。 所以从加密…...

TCP端口崩溃,msg:socket(): Too many open files

一、现象 linux系统中运行了一个TCP服务器&#xff0c;该服务器监听的TCP端口为10000。但是长时间运行时发现该端口会崩溃&#xff0c;TCP客户端连接该端口会失败&#xff1a; 可以看到进行三次握手时&#xff0c;TCP客户端向该TCP服务器的10000端口发送了SYN报文&#xff0c;…...

基于Laravel 5.6的运动健身类小程序前后端源码

基于Laravel 5.6的运动健身、健康类小程序前后端源码&#xff0c;一套比较基础的运动健康、健身类小程序源码。朋友自己无聊写的&#xff0c;比较基础&#xff0c;有需要的可以拿去修修改改升级开发一下。 使用宝塔安装&#xff0c;比较省事&#xff0c;PHP相关的扩展需要启用…...

NodeMCU ESP8266硬件开发板的熟悉

文章目录 硬件开发环境的熟悉基础介绍什么是 ESP8266 NodeMCU&#xff1f;NodeMCU芯片ESP12-E 模组开发板 ESP8266 版本引脚图Power GND I2CGPIOADCUARTSPIPWMControl 总结 硬件开发环境的熟悉 基础介绍 什么是 ESP8266 NodeMCU&#xff1f; ESP8266是乐鑫开发的一款低成本 …...

计算机毕业设计 基于SSM的在线预约导游系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…...

Mac 挂载 Alist网盘

挂载服务器的Alist 网盘到 Mac mac,使用的是 CloundMounter 这个软件进行挂载 http://ip:port/dav/ 需要在末尾加上 /dav/ 在一些服务器上&#xff0c;为了提供WebDAV服务&#xff0c;需要在URL地址的末尾添加"/dav/“。这是因为WebDAV协议规定了一些标准的URL路径&#x…...

【多模态融合】TransFusion学习笔记(1)

工作上主要还是以纯lidar的算法开发,部署以及系统架构设计为主。对于多模态融合(这里主要是只指Lidar和Camer的融合)这方面研究甚少。最近借助和朋友们讨论论文的契机接触了一下这方面的知识&#xff0c;起步是晚了一点&#xff0c;但好歹是开了个头。下面就借助TransFusion论文…...

(二)正点原子STM32MP135移植——TF-A移植

目录 一、TF-A概述 二、编译官方代码 2.1 解压源码 2.2 打补丁 2.3 编译准备 &#xff08;1&#xff09;修改Makfile.sdk &#xff08;2&#xff09;设置环境变量 &#xff08;3&#xff09;编译 三、移植 3.1 复制官方文件 3.2 修改电源 3.3 修改TF卡和emmc 3.4 添…...

将二叉搜索树转化为排序的双向链表

链接&#xff1a; ​​​​​​LCR 155. 将二叉搜索树转化为排序的双向链表 题解&#xff1a; /* // Definition for a Node. class Node { public:int val;Node* left;Node* right;Node() {}Node(int _val) {val _val;left NULL;right NULL;}Node(int _val, Node* _left…...

电脑dll丢失应该怎么解决,dll文件丢失怎么恢复方法分享

DLL&#xff08;Dynamic Link Library&#xff0c;动态链接库&#xff09;是一种可执行文件&#xff0c;它包含了在程序运行时需要调用的代码和资源。DLL 文件的主要作用是实现代码和资源的共享&#xff0c;这样在多个程序之间就可以避免重复的代码和资源&#xff0c;从而节省系…...

通达信和同花顺能否实现程序化自动交易股票,量化交易如何实现?

以下写给正在寻找自动交易接口的朋友&#xff0c;首先&#xff0c;不是那种设置个简单条件的条件单&#xff0c;或者某些客户端上形同鸡肋的策略交易&#xff0c;那些策略根本称不上策略&#xff0c;还有各种限制&#xff0c;不支持这个不支持那个&#xff0c;可设置的参数也不…...

基于Kylin的数据统计分析平台架构设计与实现

目录 1 前言 2 关键模块 2.1 数据仓库的搭建 2.2 ETL 2.3 Kylin数据分析系统 2.4 数据可视化系统 2.5 报表模块 3 最终成果 4 遇到问题 1 前言 这是在TP-LINK公司云平台部门做的一个项目&#xff0c;总体包括云上数据统计平台的架构设计和组件开发&#xff0c;在此只做…...

Linux CentOS7 vim寄存器

计算机中通常所说的寄存器Register一般指的是CPU中的寄存器&#xff0c;用来暂存CPU处理所需要的指令、数据等。 vim中同样也有寄存器&#xff0c;使用的方式和CPU非常类似。 vim中的寄存器(register)作用和windows中的剪切板类似&#xff0c;不过vim中的寄存器不止一个&…...

摄影后期图像编辑软件Lightroom Classic 2023 mac中文特点介绍

Lightroom Classic 2023 mac是一款图像处理软件&#xff0c;是数字摄影后期制作的重要工具之一&#xff0c;lrc2023 mac适合数字摄影后期制作、摄影师、设计师等专业人士使用。 Lightroom Classic 2023 mac软件特点 高效的图像管理&#xff1a;Lightroom Classic提供了强大的图…...

一种4g扫码付费通电控制器方案

之前开发了一款扫码付款通电控制器 功能&#xff1a;用户扫码付款后设备通电&#xff0c;开始倒计时&#xff0c;倒计时结束后设备断电&#xff0c;资金到账商家的商家助手里面&#xff0c;腾讯会收取千分之6手续费。 产品主要应用场景 本产品主要应用于各类无人值守或者自助…...

桌面自动化工具总结

引言:产品经理提出桌面程序需要自动化的测试,避免繁琐的人肉点击。说干就干。 现有自动化工具是五花八门,我找了两个框架。 这两个框架都是基于微软的UIA 框架,链接地址 https://learn.microsoft.com/en-us/windows/win32/winauto/uiauto-providerportal?source=recommen…...

Python入门教程 | Python 常用标准库概览

Python3 标准库概览 Python 标准库非常庞大&#xff0c;所提供的组件涉及范围十分广泛&#xff0c;使用标准库我们可以让您轻松地完成各种任务。 以下是一些 Python3 标准库中的模块&#xff1a; os 模块&#xff1a;os 模块提供了许多与操作系统交互的函数&#xff0c;例如创…...

【JavaScript】读取本地json文件并绘制表格

本文为避免跨域问题&#xff0c;使用了改造过的本地json文件的方法实现读取json数据并绘制表格。 如果发起http请求获取本地 json文件中数据&#xff0c;需要架设本地服务器&#xff0c;本文不做阐述。 概述 1、json在本地&#xff0c;并不需要从服务器下载。 2、采用jquery…...

前端笔试题总结,带答案和解析(一)

1. 执行以下程序&#xff0c;输出结果为&#xff08;&#xff09; var x 10; var y 20; var z x < y ? x:y; console.log(xx;yy;zz);A x11;y21;z11 B x11;y20;z10 C x11;y21;z10 D x11;y20;z11 初始化x的值为10&#xff0c;y的值为20&#xff0c;x < y返回结果为tru…...

LeetCode 202 快乐数

今天再次做到需要int转化成String&#xff0c;从而方便运算的题目。&#xff08;当然还可以直接使用int运算也是没问题的&#xff09; 再次出现了我容易弄混淆的问题&#xff0c;Integer.valueOf和ASCII码转化的差异&#xff1f; 其实之前我以及有记录过该问题&#xff0c;详…...

国庆作业day6

服务器 #include <my_head.h> #define IP "192.168.101.66" #define PORT 6666 int main(int argc, const char *argv[]) {//创建套接字int fd socket(AF_INET, SOCK_STREAM, 0);if(fd < 0){ERR_MSG("socket");return -1;}struct sockaddr_in s…...

李沐深度学习记录4:12.权重衰减/L2正则化

权重衰减从零开始实现 #高维线性回归 %matplotlib inline import torch from torch import nn from d2l import torch as d2l#整个流程是&#xff0c;1.生成标准数据集&#xff0c;包括训练数据和测试数据 # 2.定义线性模型训练 # 模型初始化&#xff08;函…...

堆--数组中第K大元素

如果对于堆不是太认识&#xff0c;请点击&#xff1a;堆的初步认识-CSDN博客 解题思路&#xff1a; /*** <h3>求数组中第 K 大的元素</h3>* <p>* 解体思路* <ol>* 1.向小顶堆放入前k个元素* 2.剩余元素* 若 < 堆顶元素, 则略过* …...

ipad使用技巧

1、goodnotes中批量导入pdf文件 法一&#xff1a; 直接参考视频&#xff1a; 【目前为止所知iPad上goodnotes批量导入网盘文件最快的方法】 大致步骤&#xff1a;pdf文件传到百度网盘&#xff0c;然后ES软件登录百度网盘&#xff0c;在goodnotes中导入&#xff0c;选择ES&a…...

Windows系统上使用CLion远程开发Linux程序

CLion远程开发Linux程序 情景说明Ubuntu配置CLion配置同步 情景说明 在Windows系统上使用CLion开发Linux程序&#xff0c;安装CLion集成化开发环境时会自动安装cmake、mingw&#xff0c;代码提示功能也比较友好。 但是在socket开发时&#xff0c;包含sys/socket.h头文件时&am…...

github搜索技巧

指定语言 language:java 比如我要找用java写的含有blog的内容 搜索项目名称包含关键词的内容 vue in:name 其他如项目描述跟项目文档&#xff0c;如下 组合使用 vue in:name,description,readme 根据Star 或者fork的数量来查找 总结 springboot vue stars:>1000 p…...

ui中国官网/百度笔记排名优化

《云桌面技术在高校计算机实验室中的应用(原稿).doc》由会员分享&#xff0c;可免费在线阅读全文&#xff0c;更多与《云桌面技术在高校计算机实验室中的应用(原稿)》相关文档资源请在帮帮文库(www.woc88.com)数亿文档库存里搜索。1、单位进行组织教学,并通过台或多台交换机直接…...

海外网站太慢/广州seo实战培训

Mac打字大师好用吗&#xff1f;想要练习打字&#xff1f;那就使用Master Of Typing Mac这款强大的打字练习软件吧&#xff01;master of typing mac可以提供各种练习&#xff0c;自动设置难度&#xff0c;让你快速提升打字速度&#xff0c;还提供多种测试&#xff0c;让你了解自…...

公明做网站的公司/企业查询网

ctrl 1快速修复 ctrl d 快速删除 ctrl F11快速运行 ctrl m 放大工作区 atl /注释 。。。 转载于:https://www.cnblogs.com/TreeDream/p/5475744.html...

wordpress 分类列表页/网址申请注册

Fiddler工具使用介绍一 Fiddler基础知识 Fiddler是强大的抓包工具&#xff0c;它的原理是以web代理服务器的形式进行工作的&#xff0c;使用的代理地址是&#xff1a;127.0.0.1&#xff0c;端口默认为8888&#xff0c;我们也可以通过设置进行修改。代理就是在客户端和服务器之间…...

wordpress登录名/网络营销的定义是什么

需要用到IRremote库文件 红外遥控按键16进制编码&#xff0c;使用时添加前缀 0X 红外接收 .源代码 //***************** //红外接收模块测试 //***************** #include <IRremote.h> IRrecv irrecv(6); //创建红外模块对象,并绑定红外接收模块引脚 decode_results …...

做网站要哪些技术/黑马培训机构可靠吗

滑块区间组件 功能需求&#xff1a; 最小值为0&#xff0c;按照给定的最大值&#xff0c;生成区间范围&#xff1b;拖动滑块移动时&#xff0c;显示相应的范围区间&#xff0c;滑块条显示对应的状态&#xff1b;点击时&#xff0c;使最近的滑块移动到鼠标点击的位置。 默认效…...