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

作者主页:paper jie_博客

本文作者:大家好,我是paper jie,感谢你阅读本文,欢迎一建三连哦。

本文录入于《JAVA数据结构》专栏,本专栏是针对于大学生,编程小白精心打造的。笔者用重金(时间和精力)打造,将javaSE基础知识一网打尽,希望可以帮到读者们哦。

其他专栏:《算法详解》《C语言》《javaSE》等

内容分享:本期将会分享数据结构中的优先级队列

优先级队列

我们了解过的队列,是一种先进先出的数据结构。但是呢,在有些情况下,数据的出入是有优先级的,一般出队时,可能需要优先级高的元素先出队列,在这种场景下,使用队列就不合适了。优先级就比如:我们使用手机玩游戏的时候,有电话打过来的时候,手机就要先处理打过来的电话。

在这种情况下,我们就应该提供两种基本的操作,返回最高优先级对象和新增对象。这种数据结构就是优先级队列。

优先级队列的模拟实现

JDK1.8中的priorityQueue底层使用了堆这种数据结构,而这个堆又是在完全二叉树的基础上进行的调整。

什么是堆

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

堆的性质:

堆中某个节点的值总是不大于(小根堆) 或者不小于(大根堆) 其父节点的值 

堆一定是一颗完全二叉树 

堆的存储方式

堆是一颗完全二叉树,可以用层序的规则采用顺序的方式来高效存储

这里要注意:对于不是完全二叉树的,就不适合使用顺序存储了,因为要还原二叉树,就要将空节点也保存,这样子就会浪费空间,空间利用率低。

我们可以根据以下二叉树的性质来还原二叉树:

如果i为0,表示的节点为根结点,否则i节点的双亲节点为(i - 1)/ 2

如果2 * i + 1 小于节点个数,则节点i的左孩子下标为2 * i + 1,否则没有左孩子

如果2 * i + 2 小于节点个数, 则节点i的右孩子下标为2 * i + 1,否则没有右孩

堆的创建

这里创建堆的方式是使用向下调整的方式来建堆的。想法就是从堆的最后一个节点的父结点开始向下调整,调整完后再向前一个节点向下调整,依次不断到0下标的节点调整完后就算建堆成功了。

画图分析:

建堆的时间复杂度分析

这里我们考虑最坏的情况即是一颗满叉树且每一个节点都要调整

画图分析:

通过上图得知向上调整的时间复杂度为O(n)

向下调整的复杂度会比向上调整的复杂度高上很多,不建议使用它来建堆

原因:

我们知道向上调整是从最后一层的最后一个节点开始,而向下调整只需要从倒数第二层的最后一个节点开始,而往往最后一层的节点就基本等于前面全部节点加起来之和-1

堆的插入和删除

堆的插入

堆的插入就只有4个步骤:

检查空间大小

将需插入节点放入最后一个位置

再从最后一个位置开始向上调整

最后有效位置加一

画图分析:

堆的删除

注意这里的删除是堆顶的元素。具体步骤:

将堆顶元素和堆尾元素交换

有效位置减一

从堆顶开始向下调整

画图分析:

具体代码

import java.util.Arrays;public class MyHeap {private int[] elem;int usedsize;public MyHeap() {this.elem = new int[10];}//初始化elem数组public void initElem(int[] array) {for(int i = 0; i < array.length; i++) {elem[i] = array[i];usedsize++;}}//创建大根堆public void createHeap() {for(int parent = (usedsize-1-1)/2; parent>=0; parent--) {siftDown(parent, usedsize);}}private void siftDown(int parent, int len) {//左孩子int child = 2*parent+1;while(child < len) {if(child+1 < len && elem[child] < elem[child+1] ) {child += 1;}if(elem[child] > elem[parent]) {//交换int temp = elem[child];elem[child] = elem[parent];elem[parent] = temp;parent = child;child = 2*parent+1;}else {break;}}}//插入元素public void push(int val) {//满了if(elem.length == usedsize) {elem = Arrays.copyOf(elem, elem.length * 2);}elem[usedsize] = val;siftUp(usedsize);usedsize++;}private void siftUp(int child) {int parent = (child - 1) / 2;while(child > 0 ) {if(elem[child] > elem[parent]) {int temp = elem[child];elem[child] = elem[parent];elem[parent] = temp;child = parent;parent = (child - 1) / 2;}else {break;}}}/*** 删除元素**/public void pop() {if(isEmpty()) {return ;}int temp = elem[0];elem[0] = elem[usedsize-1];elem[usedsize-1] = temp;usedsize--;int parent = 0;int child = 2 * parent + 1 ;while(child < usedsize) {if(child+1<usedsize && elem[child] < elem[child+1]) {child+=1;}if(elem[child] > elem[parent]) {int tmp = elem[child];elem[child] = elem[parent];elem[parent] = tmp;parent = child;child = 2*parent+1;}else {break;}}}public boolean isEmpty() {return usedsize == 0;}
}

priorityQueue

java集合中提供了priorityQueue和priorityBlockingQueue两种类型的优先级队列,priorityQueue是线程不安全的,PriorityQueue是线程安全的。

 

使用priorityQueue的注意事项:

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

import java.util.PriorityQueue;

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

3 不能插入null对象,不然会1抛出nullpointerException

没有容量限制,任意插入多个元素,内部会自动扩容

4 插入和删除元素的时间复杂度都是O(logn)

5 priorityQueue的底层使用堆数据结构

6 priorityQueue默认情况下是小堆---即每次获取到的元素都是最小的元素,想要建立大堆需要自己传比较器

priorityQueue的使用

优先级队列的构造方法

static void TestPriorityQueue(){
// 创建一个空的优先级队列,底层默认容量是11
PriorityQueue<Integer> q1 = new PriorityQueue<>();
// 创建一个空的优先级队列,底层的容量为initialCapacity
PriorityQueue<Integer> q2 = new PriorityQueue<>(100);
ArrayList<Integer> list = new ArrayList<>();
list.add(4);
list.add(3);
list.add(2);
list.add(1);
// 用ArrayList对象来构造一个优先级队列的对象
// q3中已经包含了三个元素
PriorityQueue<Integer> q3 = new PriorityQueue<>(list);
System.out.println(q3.size());
System.out.println(q3.peek());
}

一般在默认的情况下,PriorityQueue队列是小堆,需要变成大堆需要提供比较器:

class IntCmp implements Comparator<Integer>{
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
}

插入,删除,获取优先级最高的元素

static void TestPriorityQueue2() {int[] arr = {4, 1, 9, 2, 8, 0, 7, 3, 6, 5};
// 一般在创建优先级队列对象时,如果知道元素个数,建议就直接将底层容量给好
// 否则在插入时需要不多的扩容
// 扩容机制:开辟更大的空间,拷贝元素,这样效率会比较低PriorityQueue<Integer> q = new PriorityQueue<>(arr.length);for (int e : arr) {q.offer(e);}System.out.println(q.size()); // 打印优先级队列中有效元素个数System.out.println(q.peek()); // 获取优先级最高的元素
// 从优先级队列中删除两个元素之和,再次获取优先级最高的元素q.poll();q.poll();System.out.println(q.size()); // 打印优先级队列中有效元素个数System.out.println(q.peek()); // 获取优先级最高的元素q.offer(0);System.out.println(q.peek()); // 获取优先级最高的元素
// 将优先级队列中的有效元素删除掉,检测其是否为空q.clear();if (q.isEmpty()) {System.out.println("优先级队列已经为空!!!");}}

这里要注意优先级队列扩容说明:

如果容量小于64时,就是按oldCapacity的两倍大小来扩容

如果容量大于等于64时,就是按oldCapacity的1.5倍大小扩容

如果扩容后的容量大小大于整型的最大值,则按整型的最大值来扩容

堆的应用

用堆为数据结构来封装优先级队列

Java中的priorityQueue底层就是用堆来实现的

堆排序

堆排序就是使用堆的思想来进行排序

1. 建堆

升序:建大堆

降序:建小堆

2. 使用堆元素删除思想来进行排序

交换堆顶和堆尾元素,然后向下调整

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

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

相关文章

java最新Springboot3+微服务实战12306高性能售票系统全套开发课程

java最新Springboot3微服务实战12306高性能售票系统全套开发课程 视频课程在文末获取 第1章 课程介绍与学习指南。 1-1 课前必读&#xff08;不读错过一个亿&#xff09; 1-2 课程导学 1-3 为什么要选择最新版本SpringBoot3和JDK17&#xff1f; 1-4 在线demo网站演示 第2…

谈谈 Redis 如何来实现分布式锁

谈谈 Redis 如何来实现分布式锁 基于 setnx 可以实现&#xff0c;但是不是可重入的。 基于 Hash 数据类型 Lua脚本 可以实现可重入的分布式锁。 获取锁的 Lua 脚本&#xff1a; 释放锁的 Lua 脚本&#xff1a; 但是还是存在分布式问题&#xff0c;比如说&#xff0c;一个客…

Java_Jdbc

目录 一.JDBC概述 二.JDBC API 三.ResultSet[结果集] 四.Statement 五.PreparedStatement 六. JDBC API 总结 一.JDBC概述 JDBC 为访问不同的数据库提供了同一的接口&#xff0c;为使用着屏蔽了细节问题Java程序员使用JDBC 可以连接任何提供了 JDBC驱动的数据库系统&am…

Linux考试复习整理

文章目录 Linux考试整理一.选择题1.用户的密码现象放置在哪个文件夹&#xff1f;2.删除文件或目录的命令是&#xff1f;3.显示一个文件最后几行的命令是&#xff1f;4.删除一个用户并同时删除用户的主目录5.Linux配置文件一般放在什么目录&#xff1f;6.某文件的组外成员的权限…

双指针——复写零

一&#xff0c;题目要求 给你一个长度固定的整数数组 arr &#xff0c;请你将该数组中出现的每个零都复写一遍&#xff0c;并将其余的元素向右平移。 注意&#xff1a;请不要在超过该数组长度的位置写入元素。请对输入的数组 就地 进行上述修改&#xff0c;不要从函数返回任何东…

Python Connect SQLServer 2008

Macos&#xff08;经过了两天&#xff0c;无数次的方法验证&#xff0c;寻找各种资料&#xff0c;总结如下&#xff09; brew install freetds0.91 如果出现错误就进行手工安装&#xff0c;也可以直接使用 brew install freetds安装最新版本&#xff08;测试通过&#xff09; …

Kerberos认证协议介绍

概述 官网&#xff1a;https://www.kerberos.org/ 官方文档&#xff1a;http://web.mit.edu/kerberos/krb5-current/doc/ 为TCP/IP网络系统设计的可信的第三方身份认证协议。网络上的Keberos服务基于DES对称加密算法&#xff0c;但也可以用其他算法替代。因此&#xff0c;Keb…

CSS 基础知识-01

CSS 基础知识 1.CSS概述2. CSS引入方式3. 选择器4.文字控制属性5. 复合选择器6. CSS 特性7.背景属性8.显示模式9.选择器10.盒子模型 1.CSS概述 2. CSS引入方式 3. 选择器 4.文字控制属性 5. 复合选择器 6. CSS 特性 7.背景属性 8.显示模式 9.选择器 <!DOCTYPE html> <…

(※)力扣刷题-栈和队列-用栈实现队列

使用栈实现队列的下列操作&#xff1a; push(x) – 将一个元素放入队列的尾部。pop() – 从队列首部移除元素。peek() – 返回队列首部的元素。empty() – 返回队列是否为空。 说明: 你只能使用标准的栈操作 – 也就是只有 push to top, peek/pop from top, size, 和 is empt…

【Java基础面试三十一】、String a = “abc“; ,说一下这个过程会创建什么,放在哪里?

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;String a “abc”; &am…

Java集合类

Java集合类 集合类 集合类其实就是为了更好地组织、管理和操作我们的数据而存在的&#xff0c;包括列表、集合、队列、映射等数据结构。 集合根接口 Java中已经帮我们将常用的集合类型都实现好了&#xff0c;我们只需要直接拿来用就行了 所有的集合类最终都是实现自集合根…

【Javascript保姆级教程】运算符

文章目录 前言一、运算符是什么二、赋值运算符2.1 如何使用赋值运算符2.2 示例代码12.3 示例代码2 三、自增运算符3.1 运算符3.2 示例代码13.3 示例代码2 四、比较运算符4.1 常见的运算符4.2 如何使用4.3 示例代码14.4 示例代码2 五、逻辑运算符逻辑运算符列举 六、运算符优先级…

数据预处理—滑动窗口采样数据

一个简单的例子&#xff1a; # data: 这是要应用滑动窗口采样的输入数据&#xff0c;通常是一个序列&#xff0c;例如列表或NumPy数组。 # window_size: 这是滑动窗口的大小&#xff0c;表示每个窗口中包含的元素数量。 # step_size: 这是滑动窗口移动的步长&#xff0c;表示每…

变化检测数据集制作详细版

本文记录在进行变化检测数据集制作过程中所使用的代码 首先需要准备相同地区不同时间的两幅影像&#xff0c;裁减成合适大小&#xff0c;如256*256。相同区域命名相同放在两个文件夹下。 接着使用labelme对变化区域进行标注&#xff0c;这里不再进行labelme安装及标注的赘述。…

支持语音与视频即时通讯项目杂记(一)

第一部分解释服务端的实现。 &#xff08;服务端结构&#xff09; 下面一个用于实现TCP服务器的代码&#xff0c;包括消息服务器&#xff08;TcpMsgServer&#xff09;和文件中转服务器&#xff08;TcpFileServer&#xff09;。 首先&#xff0c;TcpServer是TcpMsgServer和Tcp…

语音识别whisper的介绍、安装、错误记录

介绍 Whisper是OpenAI于2022年9月份开源的通用的语音识别模型。它是在各种音频的大型数据集上训练的模型&#xff0c;也是一个可以执行多语言语音识别、语音翻译和语言识别的多任务模型。 论文链接&#xff1a;https://arxiv.org/abs/2212.04356 github链接&#xff1a;https:…

手部关键点检测4:Android实现手部关键点检测(手部姿势估计)含源码 可实时检测

目录 1. 前言 2.手部关键点检测(手部姿势估计)方法 (1)Top-Down(自上而下)方法 (2)Bottom-Up(自下而上)方法&#xff1a; 3.手部关键点检测模型训练 4.手部关键点检测模型Android部署 &#xff08;1&#xff09; 将Pytorch模型转换ONNX模型 &#xff08;2&#xff09; …

日常中msvcp71.dll丢失怎样修复?分享5个修复方法

在 Windows 系统中&#xff0c;msvcp71.dll 是一个非常重要的动态链接库文件&#xff0c;它承载了许多应用程序和游戏的运行。如果您的系统中丢失了这个文件&#xff0c;那么您可能会遇到无法打开程序、程序崩溃或出现错误提示等问题。本文将介绍 5 个快速修复 msvcp71.dll 丢失…

Linux —— 网络基础(一)

目录 一&#xff0c;计算机网络背景 二&#xff0c;网络协议初识 三&#xff0c;网络传输基本流程 四&#xff0c;网络中的地址管理 一&#xff0c;计算机网络背景 网络发展 独立模式&#xff0c;计算机之间相互独立&#xff1b;网络互联&#xff0c;多台计算机连接在一起…

新手如何找到Docker容器(redis)中的持久化文件?

具体步骤 要查看Docker容器的dump.rdb和appendonly.aof文件&#xff08;如果启用了AOF持久化&#xff09;的位置&#xff0c;我们需要知道容器中Redis配置文件的内容或者容器的数据卷的挂载位置。 这里是一般步骤&#xff1a; 查找容器的数据卷挂载位置 使用docker inspect命令…