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

文章目录

    • 1.优先级队列概念 💮
    • 2.优先级队列的模拟实现💮
    • 3.常用接口PrinrityQueue介绍💮
    • 4.堆的应用💮

1.优先级队列概念 💮

优先级队列 :是不同于先进先出队列的另一种队列。每次从队列中取出的是具有最高优先权的元素。

优先级队列相对于普通队列应该提供两个最基本的操作
(1)返回最高优先级对象(2)添加新的对象

2.优先级队列的模拟实现💮

在JDk1.8中的优先级队列底层使用了堆,而堆实际就是在完全二叉树的基础上进行了一些调整。

2.1堆的概念💮

堆这种数据结构本质上就是一个完全二叉树
并且堆中某个结点的值总是不大于或不小于其父结点的值

小堆:根节点最小的堆,满足Ki <= K2i+1 且 Ki <= K2i+2

大堆:根节点最大的堆, 满足Ki >= K2i+1 且 Ki >= K2i+2

在这里插入图片描述

🌹 堆的性质

  • 堆中某个节点的值总是不大于或不小于其父节点的值;
  • 堆总是一棵完全二叉树。

2.2堆的存储方式💮

堆是一棵完全二叉树,因此可以层序的规则采用顺序的方式来高效存储
对于非完全二叉树,则不适合使用顺序方式进行存储,因为为了能够还原二叉树,空间中必须要存储空节 点,就会导致空间利用率比较低。

i为孩子节点,双亲节点为 (i - 1)/2
i为根节点,左孩子下标为2 * i + 1,右孩子下标为2 * i + 2

2.3堆的创建💮

2.3.1堆向下调整(以创建大堆为例)💮

分析过程如下:
在这里插入图片描述

     //向下调整private void shiftDown(int parent, int len) {int child = 2 * parent + 1;//最起码要有左孩子while (child < len) {//child+1<len的设置是为了防止child越界异常//获得左右孩子的最大值if (child + 1 < len && elem[child] < elem[child + 1]) {child++;}//child下标一定是左右孩子  最大值的下标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 createHeap() {//usedSize-1表示最后一个child,(usedSize-1-1)/2表示最后一个父节点for (int parent = (usedSize - 1 - 1) / 2; parent >= 0; parent--) {shiftDown(parent, usedSize);}}

时间复杂度分析
第1层需要向下移动h-1层
第2层需要向下移动h-2层
…依次类推

分析过程如下:


在这里插入图片描述
2.4堆的插入与删除💮
2.4.1堆的插入💮
分析过程如下:

这里以在大根堆前提下插入80为例:

在这里插入图片描述

代码如下:

//向上调整private void shiftUp(int child) {int parent = (child - 1) / 2;while (child > 0) {if (elem[child] > elem[parent]) {int tmp = elem[child];elem[child] = elem[parent];elem[parent] = tmp;child = parent;parent = (child - 1) / 2;} else {break;}}}//插入元素   向上调整public void offer(int val) {if (isFull()) {//扩容elem = Arrays.copyOf(elem, 2 * elem.length);}elem[usedSize++] = val;//11//向上调整shiftUp(usedSize - 1);//10}public boolean isFull() {return usedSize == elem.length;}

2.4.2堆的删除💮
分析过程如下:
在这里插入图片描述
代码如下:

需要判断堆中元素是否为空的情况

//删除元素public void pop() {if (isEmpty()) {return;}swap(elem, 0, usedSize - 1);usedSize--;shiftDown(0, usedSize);}public void swap(int[] array, int i, int j) {int tmp = array[i];array[i] = array[j];array[j] = tmp;}public boolean isEmpty() {return usedSize == 0;}

选择题练习

4.最小堆[0,3,2,5,7,4,6,8],在删除堆顶元素0之后,其结果是( C )
A: [3,2,5,7,4,6,8] B: [2,3,5,7,4,6,8]
C: [2,3,4,5,7,8,6] D: [2,3,4,5,6,7,8]
在这里插入图片描述

3.常用接口PrinrityQueue介绍💮

3.1PrinrityQueue的特性

Java集合框架中提供了PriorityQueue和PriorityBlockingQueue两种类型的优先级队列,PriorityQueue是线
程不安全的,PriorityBlockingQueue是线程安全的,本文主要介绍PriorityQueue。

在这里插入图片描述

使用注意:

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

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

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

  4. 插入和删除元素的时间复杂度为

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

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

3.2 PriorityQueue常见的几种构造方式
在这里插入图片描述

static void TestPriorityQueue(){ 
// 创建一个空的优先级队列,底层默认容量是11  PriorityQueue<Integer> q1 = new PriorityQueue<>(); 
// 创建一个空的优先级队列,底层的容量为initialCapacityPriorityQueue<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队列是小堆,如果需要大堆需要提供比较器

// 用户自己定义的比较器:直接实现Comparator接口,然后重写该接口中的compare方法即可
class IntCmp implements Comparator<Integer>{ @Override
public int compare(Integer o1, Integer o2) { return o2-o1;} }
public class TestPriorityQueue {
public static void main(String[] args) { PriorityQueue<Integer> p = new PriorityQueue<>(new IntCmp());p.offer(4); p.offer(3);p.offer(2);p.offer(1); p.offer(5);System.out.println(p.peek()); } }

此时创建出来的就是一个大堆。

插入删除/获取优先级最高的元素
在这里插入图片描述
练习:top-k问题

描述:求前k个最大的数

适用情况: 在数据量比较大时,求数据集合中前K个最大的元素或者最小的元素

思路:

1)求前K个最大的元素,建立大小为K的小根堆
2)然后用剩下的集合里面的元素轮流和堆顶元素比较,如果剩下集合里面的元素比堆顶的元素大,那就替换掉堆顶的元素
3)然后向下调整,变成新的小根堆,此时这个堆中的元素就是前K个最大元素

代码如下:

public class Test {//前k个最大的数public static int[] maxK(int[] arr, int k) {int[] ret = new int[k];if (arr == null || k == 0) {return ret;}Queue<Integer> minHeap = new PriorityQueue<>(k);//总的时间复杂度:K * logK + (N-K) * logK = NlogK//时间复杂度:K * logK//1.遍历数组的前k个 放到堆当中for (int i = 0; i < k; i++) {minHeap.offer(arr[i]);}//2.遍历剩下的K-1个,每次和堆顶元素进行比较//堆顶元素 小的时候,就出堆//时间复杂度 : (N-K) * logKfor (int i = k; i < arr.length; i++) {int val = minHeap.peek();if (val < arr[i]) {minHeap.poll();minHeap.offer(arr[i]);}}for (int i = 0; i < k; i++) {ret[i] = minHeap.poll();}return ret;}public static void main(String[] args) {int[] array = {1, 5, 43, 3, 2, 7, 98, 41, 567, 78};int[] ret = maxK(array, 3);System.out.println(Arrays.toString(ret));}
}

如果要求前K个最小的元素,如何做?

和前面差不多,不同的是
(1)求前K个最小的元素,要建立大根堆
(2)比较的时候谁小,就把小的放在堆顶

面试题17.14.求前k个最小数-力扣(LeetCode)

class Solution {public int[] smallestK(int[] arr, int k) {int[] ret = new int[k];if (arr == null || k == 0) {return ret;}//PriorityQueue默认建立小根堆,但是这里我们要建立大根堆,就需要自己实现比较器Queue<Integer> minHeap = new PriorityQueue<>(k,new  Comparator<Integer>() {@Override//o1-o2就是小根堆public int compare(Integer o1, Integer o2) {return o2-o1;}});//总的时间复杂度:K * logK + (N-K) * logK = NlogK//时间复杂度:K * logK//1.遍历数组的前k个 放到堆当中for (int i = 0; i < k; i++) {minHeap.offer(arr[i]);}//2.遍历剩下的K-1个,每次和堆顶元素进行比较//堆顶元素 大的时候,就出堆//时间复杂度 : (N-K) * logKfor (int i = k; i < arr.length; i++) {int val = minHeap.peek();if (val > arr[i]) {minHeap.poll();minHeap.offer(arr[i]);}}for (int i = 0; i < k; i++) {ret[i] = minHeap.poll();}return ret;}
}

4.堆的应用💮

4.1 PriorityQueue的实现

用堆作为底层结构封装优先级队列

4.2堆排序

堆排序即利用堆的思想来进行排序,总共分为两个步骤:

  1. 建堆
  • 升序:建大堆
  • 降序:建小堆
  1. 利用堆删除思想来进行排序
    即将第一个元素与最后一个元素交换,从上往下调整为大根堆/小根堆,最后输出就是一个有序序列

建堆和堆删除中都用到了向下调整,因此掌握了向下调整,就可以完成堆排序。

这里要按照从小到大排序,就建立大根堆

 public static void heapSort(int[] array) {createBigHeap(array);int end = array.length - 1;while (end > 0) {swap(array, 0, end);shiftDown(array, 0, end);end--;}}private static void createBigHeap(int[] array) {for (int parent = (array.length - 1 - 1) / 2; parent >= 0; parent--) {shiftDown(array, parent, array.length);}}private static void shiftDown(int[] array, int parent, int len) {int child = parent * 2 + 1;//至少有左孩子while (child < len) {if (child + 1 < len && array[child] < array[child + 1]) {//有右孩子,且右孩子最大child++;}if (array[child] > array[parent]) {swap(array, child, parent);parent = child;child = 2 * parent + 1;} else {//child比parent小,不需要调整break;}}}

同样的,如果要从大到小排序,就要建立小根堆

练习:
在这里插入图片描述
分析:
在这里插入图片描述

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

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

相关文章

CAD中让时间日期自动填写的方法

图纸的图签中&#xff0c;通常会有一栏是出图日期。有的单位&#xff0c;也会叫做版本号。即哪天出的图。一般情况下&#xff0c;出图日期就是打图当天。 在这样的前期下&#xff0c;图纸由于存在频繁修改&#xff0c;所以出图日期也会存在变化。还有一种情况&#xff0c;就是出…

Docker Compose 实现单机容器集群编排管理

目录 Docker ComposeDocker Compose 三大概念1. Docker Compose 环境安装2. YAML 文件格式及编写注意事项3. Docker Compose配置restart 设置重启策略&#xff0c;no&#xff0c;always&#xff0c;no-failure&#xff0c;unless-stopped 4. Docker Compose 常用命令选项5. Doc…

基础算法-数组模拟栈

栈&#xff1a;先进后出 队列&#xff1a;先进先出 栈 例题&#xff1a; 实现一个栈&#xff0c;栈初始为空&#xff0c;支持四种操作&#xff1a; push x – 向栈顶插入一个数 x&#xff1b;pop – 从栈顶弹出一个数&#xff1b;empty – 判断栈是否为空&#xff1b;query …

CPU渲染or GPU渲染,你选对了吗?看完这六点就懂了!

在进行动画或效果图渲染时&#xff0c;选择适合的渲染方式对于项目的速度和质量至关重要。CPU渲染和GPU渲染作为两种主要的渲染方式&#xff0c;哪一种更适合你现在的情况&#xff1f;接下来我将从以下六个方面带大家深入了解&#xff0c;看完就知道怎么选了。 1.渲染原理 CPU…

OS1_进程与线程的管理

序言 1.OS以进程、线程的方式在CPU中执行静态保存在外存(内存)中的程序&#xff0c;进程的构成与状态转化&#xff0c;特别是进程的切换&#xff1b; 2.当有多个进程处于就绪态&#xff0c;有哪些常见的挑选以执行方式&#xff1b; 3.并发执行(乱序发射)的进程&#xff0c;共享…

Pytorch深度学习------torchvision中dataset数据集的使用(CIFAR10)

文章目录 一、什么是TorchVision二、以torchvision.datasets子模块下的CIFAR10数据集为例1、CIFAR10数据集参数2、代码中使用 一、什么是TorchVision torchvision是pytorch的一个图形库&#xff0c;用来处理图像&#xff0c;主要用来构建计算机视觉模型。 从下面的官网截图可以…

TCP/UDP的首部

TCP/UDP首部信息 TCP首部第一个4字节第二个4字节与第三个4字节第四个4字节第五个4字节选项最大报文段长度&#xff08;MSS&#xff09;选项窗口扩大选项时间戳选项 什么时候发送RST包UDP首部 TCP首部 TCP 首部长度为20字节&#xff0c;加上选项部分最大可达60字节。 第一个4…

微服务远程调用openFeign简单回顾

目录 一. OpenFeign简介 二. OpenFeign原理 演示使用 provider模块 消费者模块 配置全局feign日志 示例源代码: 一. OpenFeign简介 OpenFeign是SpringCloud服务调用中间件&#xff0c;可以帮助代理服务API接口。并且可以解析SpringMVC的RequestMapping注解下的接口&#x…

接口漏洞-DVWS(XXE+鉴权)+阿里云KEY

dvws靶场 靶场&#xff1a;https://github.com/snoopysecurity/dvws-node 开启靶场&#xff0c;注册一个普通用户&#xff0c;登录成功 来到首页点击admin area 发现进入几秒钟之后又跳转回到首页&#xff0c;只有管理员admin用户才能进入。 点击admin area抓包&#xff0c;发现…

C# List 详解四

目录 18.FindLast(Predicate) 19.FindLastIndex(Int32, Int32, Predicate) 20.FindLastIndex(Int32, Predicate) 21.FindLastIndex(Predicate) 22.ForEach(Action) 23.GetEnumerator() 24.GetHashCode() 25.GetRange(Int32, Int32) C#…

实验二十四、滞回比较器电压传输特性的测量

一、题目 滞回比较器电压传输特性的测量。 二、仿真电路 电路如图1所示。 为便于观察电压传输特性的变化&#xff0c;输入信号采用信号发生器产生的幅值为 10 V 10\,\textrm V 10V、频率为 20 Hz 20\,\textrm{Hz} 20Hz 的三角波电压。采用虚拟的运算放大电路&#xff0c;其…

js中的数据结构之栈

js中的数据结构之栈 一、定义 栈是一种限定仅在表尾进行插入和删除操作的线性表&#xff0c;遵循后入先出的原则。 二、描述 栈是和列表类似的一种数据结构&#xff0c;也可以说栈就是一种特殊的列表。 栈里面的元素只能通过列表的一端访问&#xff0c;这一端称为栈顶。相…

树莓派 使用 docker安装showdoc

最近在使用showdoc时感觉挺方便的&#xff0c;但是在树莓派上拉取showdoc对应的镜像时特别慢&#xff0c;熬了一天半发现镜像倒在了最后一步&#xff0c;重试了好几次都没有拉去下来。后来仔细阅读了官网操作文档再加一点小运气今天成功给安装成功了。 showdoc的安装环境&…

Windows系统实现唤醒+合成+命令词智能语音交互

1、之前写过离线能力调用&#xff0c;今天来个终极版&#xff0c;实现智能交互或者结合大模型的智能交互示例&#xff0c;下面进入正题。上B站效果离线唤醒离线合成离线命令词实现智能交互_哔哩哔哩_bilibili 2、到讯飞开放平台下载唤醒合成命令词的离线组合包&#xff0c;找到…

【趟坑记录】d3.zoom()的正确使用姿势 @d3.v7

【趟坑记录】d3.zoom()的正确使用姿势 d3.v7 文章目录 【趟坑记录】d3.zoom()的正确使用姿势 d3.v7问题重现原因分析解决方案放缩平移写法特殊修改transform函数的写法 总结 在开发一个D3应用的时候遇到了一个 zoom相关的问题&#xff0c;记录解决思路与方案 问题重现 最近在…

Windows 10, version 22H2 (updated Jul 2023) 中文版、英文版下载

Windows 10, version 22H2 (updated Jul 2023) 中文版、英文版下载 Windows 10 22H2 企业版 arm64 x64 请访问原文链接&#xff1a;https://sysin.org/blog/windows-10/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org Window…

FPGA——verilog实现格雷码与二进制的转换

文章目录 一、格雷码简介二、二进制转格雷码三、格雷码转二进制四、仿真 一、格雷码简介 格雷码是一种循环二进制码或者叫作反射二进制码。跨时钟域会产生亚稳态问题&#xff08;CDC问题&#xff09;&#xff1a;从时钟域A过来的信号难以满足时钟域B中触发器的建立时间和保持时…

python实现远程服务器的操作

前言 测试过程中经常会遇到需要将本地的文件上传到远程服务器上&#xff0c;或者需要将服务器上的文件拉到本地就行操作&#xff0c;以前安静经常会用到xftp工具。今天介绍一种python库Paramiko&#xff0c;可以帮助我们通过代码的方式进行完成对远程服务器的上传和下载操作。…

elementui el-table 封装表格

ps: 1.3版本 案例&#xff1a; 完整代码&#xff1a; 可直接复制粘贴&#xff0c;但一定要全看完&#xff01; v-slot"scopeRows" 是vue3的写法&#xff1b; vue2是 slot-scope"scope" <template><!-- 简单表格、多层表头、页码、没有合并列行…

flutter开发实战-Stagger Animation实现水波纹动画

flutter开发实战-实现水波纹动画&#xff0c;使用到了交织动画&#xff0c;实现三个圆逐渐放大与渐变的过程。 一、效果图 二、实现水波纹效果 实现水波纹动画&#xff0c;使用到了交织动画&#xff0c;实现三个圆逐渐放大与渐变的过程。 交织动画 有些时候我们可能会需要一些…