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

Java——优先级队列(堆)

优先级队列

    • 堆的概念
    • 堆的存储方式
    • 堆的创建
    • 堆的插入和删除
  • PriorityQueue优先级队列
    • 优先级队列方法
  • 最小的K个数

前言
前面已经知道了,队列是先进先出,那如果想让数据具有优先级,有时候可能需要按照优先级来进行出队列,例如:将队列中最小元素或者最大元素出去,难道我们要遍历找到吗,引入了优先级队列(PriorityQueue)


优先级队列PriorityQueue底层是使用的是这种数据结构,而堆又是在完全二叉树的基础上进行调整,所以先介绍堆

堆的概念

就是有一个集合,将其中所有元素按照完全二叉树的顺序存储在一个一维数组中,就是将一棵二叉树层序遍历的结果放入一个一维数组中
但是,这个完全二叉树只有大堆和小堆两种情况
大根堆:堆的某个节点的值总是不大于其父亲点的值,根节点值最大
小根堆:堆的某个节点的值总是不小于其父亲点的值,根节点值最小
堆是一棵完全二叉树

在这里插入图片描述

堆的存储方式

堆是一棵完全二叉树,采用是层序遍历方式高效的存储

为什么必须是完全二叉树呢
因为完全二叉树是按照从上到下,从左到右依次存放,更加高效
但非完全二叉树存放的时候有的地方存放的是空的,存在空间浪费
在这里插入图片描述

堆的创建

堆分为大堆和小堆,这里我们以小堆来讲解
在这里插入图片描述
在这里插入图片描述

这里我们采取的是向下调整
小堆:就是从最后一个父亲节点开始,也就是从28节点开始,每个节点都要满足是其下面节点中的最小值,注意每一棵树的子树又可以看成一颗完整的数,所以这里再调整上面节点的时候可能会影响到下面,因此每次调整完上面,还要继续向下判断,调整
在调整以parent为根的⼆叉树时,必须要满⾜parent的左⼦树和右⼦树已经是堆了才可以向下
调整

public void shiftDown(int[] array, int parent) {//child判断有没有左右孩子int child = 2 * parent + 1;int size = array.length;while (child < size) {// 如果右孩⼦存在,找到左右孩⼦中较⼩的孩⼦,⽤child进⾏标记 if(child+1 < size && array[child+1] < array[child]){child += 1;}//如果父亲的节点值大于child节点对应的值就if (array[parent] <= array[child]) {break;}else{// 将双亲与较⼩的孩⼦交换 int t = array[parent];array[parent] = array[child];array[child] = t;// parent中⼤的元素往下移动,可能会造成⼦树不满⾜堆的性质,因此需要继续向下调整parent = child;child = parent * 2 + 1;}}}

建堆的时间复杂度为O(N)

在这里插入图片描述

堆的插入和删除

堆的插入

1.将这个要插入的元素放入最底层
2.采用向上调整,直到满足堆的性质
在这里插入图片描述
这里原本是小堆,这里插入一个10元素,这时候就要调整了,因为10<28所以这里就要将10向上调整,这里只要判断与其父亲节点大小判断就行,因为原本已经按照小堆创建好了,这里不像创建堆的那个向下调整,这里只要将孩子和父亲节点比较就行,并且这里的调整上面不会影响到下面

堆的删除
这里删除的是堆顶元素,删除后,肯定要调整
1.先将堆顶元素与堆底元素进行交换
2.将堆元素减少一个(将最后一个元素舍弃)
3.向下调整(此时不满足堆的性质)

在这里插入图片描述
例如:这里进行堆的删除,肯定是删除10

  1. 先将10 与 28进行交换
  2. 删除最后一个元素
  3. 此时不满足小堆的性质,因此需要从堆顶向下调整,直到满足堆的性质为止

1.下列关键字序列为堆的是:(A)
A: 100,60,70,50,32,65 B: 60,70,65,50,32,100 C: 65,100,70,32,50,60
D: 70,65,100,32,50,60 E: 32,50,100,70,65,60 F: 50,100,70,65,60,32
堆只要两种,大根堆和小根堆
A是满足大根堆,其他选型都不满足

在这里插入图片描述

2.已知⼩根堆为8,15,10,21,34,16,12,删除关键字8之后需重建堆,在此过程中关键字之间的⽐较次数是( C ) A: 1 B: 2 C: 3 D: 4

在这里插入图片描述
进行3次比较,所以选C

3.最⼩堆[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]

在这里插入图片描述
所以这里层序遍历结果为2 3 4 5 7 8 6选 C

PriorityQueue优先级队列

Java集合框架中提供了PriorityQueuePriorityBlockingQueue两种类型的优先级队列,
PriorityQueue是线程不安全的PriorityBlockingQueue是线程安全的
下面介绍的是PriorityQueue
1.PriorityQueue中放置的元素必须要能够⽐较⼤⼩,不能插⼊⽆法⽐较⼤⼩的对象,否则会抛出
ClassCastException异常
2. 不能插⼊null对象,否则会抛出NullPointerException
3. 没有容量限制,可以插⼊任意多个元素,因为其内部是可以自动扩容的
4. PriorityQueue默认情况下是⼩堆—即每次获取到的元素都是最⼩的元素

优先级队列方法

public PriorityQueue()无参构造,默认容量为11
public PriorityQueue(int initialCapacity)初始容量为 initialCapacity>=1
public PriorityQueue(Comparator<? super E> comparator)传比较器的构造

无参构造

PriorityQueue默认情况下是⼩堆---即每次获取到的元素都是最⼩的元素
public class Test {public static void main(String[] args) {PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();priorityQueue.offer(3);priorityQueue.offer(2);priorityQueue.offer(1);System.out.println("堆顶元素:"+priorityQueue.peek());}
}

运行结果如下
我们可以知道其默认是按照小堆排放的,因为我们这里获取堆顶元素是1
在这里插入图片描述
那我们可以使用大堆吗,这时就要用到传比较器的构造方法了

class IntCmp implements Comparator<Integer>{@Overridepublic int compare(Integer o1,Integer o2){return o2 - o1;}
}
public class Test {public static void main(String[] args) {PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(new IntCmp());priorityQueue.offer(3);priorityQueue.offer(2);priorityQueue.offer(1);System.out.println("堆顶元素:"+priorityQueue.peek());}
}

此时传了创建大根堆的比较器,变成大根堆了
在这里插入图片描述

成员方法
> 这里是引用

boolean offer(E e) 插入元素e,并且如果空间不够这里编译器会自动扩容
E peek() ,获取堆顶元素
E poll() ,将堆顶元素出堆

public class Test {public static void main(String[] args) {PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();priorityQueue.offer(3);priorityQueue.offer(2);priorityQueue.offer(1);priorityQueue.offer(22);priorityQueue.offer(33);System.out.println("堆顶元素:"+priorityQueue.peek());//获取堆顶元素priorityQueue.poll();//出堆顶元素System.out.println("堆顶元素:"+priorityQueue.peek());//获取堆顶元素}
}

运行结果如下
这里默认是小根堆,这里堆顶元素是,将堆顶元素出去,以后就成了堆顶元素
在这里插入图片描述

但要注意

1.PriorityQueue中放置的元素必须要能够⽐较⼤⼩,不能插⼊⽆法⽐较⼤⼩的对象,否则会抛出
ClassCastException异常

这里我们如果给他传一个类类型的数据,这时候进行offer的时候就会报错,因为我们的类,类型并不知道如何比较,因此我们可以再类内实现
在这里插入图片描述

class Student  {int age;Student(int age){this.age = age;}@Overridepublic String toString() {return "Student{" +"age=" + age +'}';}
}
public class Test {public static void main(String[] args) {//大根堆PriorityQueue<Student> priorityQueue = new PriorityQueue<>(new Comparator<Student>() {@Overridepublic int compare(Student o1, Student o2) {return o2.age - o1.age;}});priorityQueue.offer(new Student(3));priorityQueue.offer(new Student(2));priorityQueue.offer(new Student(1));System.out.println(priorityQueue.peek());}
}

这样就可以以age进行比较来建堆了,这里是大堆
在这里插入图片描述

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

在这里插入图片描述

int size() 获取元素有效个数,由于默认为11容量,可能有存在不用的空间

public class Test {public static void main(String[] args) {PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();priorityQueue.offer(3);priorityQueue.offer(2);priorityQueue.offer(1);priorityQueue.offer(22);priorityQueue.offer(33);System.out.println("有效元素个数:"+priorityQueue.size());}
}

虽然这里默认容量为11,但是我们只使用了5个,有效的元素个数为5
在这里插入图片描述

最小的K个数

在这里插入图片描述

目的:在arr数组中找出其中最小的k个数
思路1 :可以使用PriorityQueue将arr数组中的所有元素,在创建K个大小的临时数组来接收前K个元素,这是因为我们已经知道原本的是默认是创建为小堆,所以每当放入一个元素都会调整
但是这种方法浪费空间和时间
思路2:我们如果获取其前K个最小的,其实可以创建一个大堆
1.先把前K个元素放入堆中,这里是大堆,堆顶元素是其中最大的
2.遍历后面剩下的,如果有小于堆顶的,就说明堆顶不是前K个最小的,就进行交换
并且题目中说的是不要求顺序,只要是前k个最小的就行

在这里插入图片描述
在这里插入图片描述

class Solution {public int[] smallestK(int[] arr, int k) {int[] ret = new int[k];if(arr==null||k<=0){return ret;}PriorityQueue<Integer> maxHeap = new PriorityQueue<>(new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return o2 - o1;}});//1.使用大堆将前K个元素放入进去for(int i = 0;i<k;i++){maxHeap.offer(arr[i]);}for(int i = k;i<arr.length;i++){//如果小于其中最大的,就进行出堆,入堆if(maxHeap.peek()>arr[i]){maxHeap.poll();maxHeap.offer(arr[i]);}}for(int i = 0;i<k;i++){ret[i] = maxHeap.poll();}return ret;}
}

相关文章:

  • 明阳智慧能源社招校招入职测评 |iLogic言语逻辑数字、Talent5大五职业性格测评、TAS倍智人才测评考什么
  • JVM规范之栈帧
  • 我的第1个爬虫程序——豆瓣Top250爬虫的详细步骤指南
  • uni-app学习笔记十二-vue3中组件传值(对象传值)
  • 字节跳动GPU Scale-up互联技术白皮书
  • 【数据结构】实现方式、应用场景与优缺点的系统总结
  • 基于Android的军训app的设计与实现
  • Leetcode 刷题记录 10 —— 二叉树
  • OpenGL环境配置
  • c++ overwrite
  • OpenCV 第7课 图像处理之平滑(二)
  • springboot中拦截器配置使用
  • windows和mac安装虚拟机-详细教程
  • 新闻推荐预测系统实战指南
  • html学习
  • 前端流行框架Vue3教程:27. 依赖注入
  • 教师技术知识对人工智能赋能下教学效果的影响:以教学创新为中介的实证研究
  • UART、RS232、RS485基础知识
  • L1-110 这不是字符串题 - java
  • JWT令牌详解及Java中的使用实战
  • 甘肃省建设厅官方网站/杭州seo整站优化
  • 怎么做劳务公司网站/国内打开google网页的方法
  • 上海opencart网站建设/网站推广的具体方案
  • 营销型网站制作服务商/传媒网站
  • 东莞高端品牌网站建设/微信管理系统登录入口
  • 网站被黑/企业网站排名优化公司