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

 博主主页: 码农派大星.

数据结构专栏:Java数据结构

关注博主带你了解更多数据结构知识


1. 优先级队列

1.1 概念

前面介绍过队列,队列是一种先进先出(FIFO)的数据结构,但有些情况下,操作的数据可能带有优先级,一般出队 列时,可能需要优先级高的元素先出队列,该中场景下,使用队列显然不合适,比如:在手机上玩游戏的时候,如果有来电,那么系统应该优先处理打进来的电话.

在这种情况下,数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象。这种数 据结构就是优先级队列(Priority Queue)。

1.2 优先级队列的模拟实现

JDK1.8中的PriorityQueue底层使用了堆这种数据结构,而堆实际就是在完全二叉树的基础上进行了一些调整。

2.堆的概念

2.1堆的概念

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

堆的性质:

1.堆中某个节点的值总是不大于或不小于其父节点的值;

2.堆总是一棵完全二叉树。

 2.2 堆的存储方式      

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

注意:

对于非完全二叉树,则不适合使用顺序方式进行存储,因为为了能够还原二叉树,空间中必须要存储空节点,就会导致空间利用率比较低。 

将元素存储到数组中后,可以根据二叉树的性质对树进行还原。假设i为节点在数组中的下标,则有

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

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

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

2.3 堆的创建 

以大根堆为例:

向下调整:从最后一个父节点:(usedsize-1-1)/2的位置开始到0,到0位置结束。向下遍历时候如果父节点大于孩子节点就交换

插入元素的时候,插入到最后一个再向上调整

出元素的时候,出的是最大的元素,那么久直接让坐标0的元素和最后一个元素进行交换,再向下调整。

import java.util.Arrays;
public class TestHeap {public int[] elem;public int usedSize;public TestHeap() {this.elem = new int[10];}public void init(int[] array) {for (int i = 0; i < array.length; i++) {elem[i] = array[i];usedSize++;}}//把elem数组当中的数据 调整为大根堆  public void createHeap() {for (int parent = (usedSize - 1 - 1) / 2; parent >= 0; parent--) {siftDown(parent, usedSize);}}private void swap(int i, int j) {int tmp = elem[i];elem[i] = elem[j];elem[j] = tmp;}public void siftDown(int parent, int end) {int child = 2 * parent + 1;while (child < end) {if (child + 1 < end && elem[child] < elem[child + 1]) {child++;}//child下标 就是 左右孩子的最大值if (elem[child] > elem[parent]) {swap(child, parent);parent = child;child = 2 * parent + 1;} else {break;}}}public boolean isFull(){return usedSize == elem.length;}//插入public void offer(int val){if(isFull()){elem = Arrays.copyOf(elem,2*elem.length);}elem[usedSize] = val;usedSize++;siftUp(usedSize-1);}public void siftUp(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 boolean isEmpty(){return usedSize == 0;}//删除public int poll(){if(isEmpty()){return -1;}int old = elem[0];swap(0,usedSize-1);usedSize++;siftDown(0,usedSize);return old;}
}

3.PriorityQueue

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

PriorityQueue的使用要注意:

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

import java.util.PriorityQueue;

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

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

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

5. 插入和删除元素的时间复杂度为O(\log_{2}N)

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

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

3.1PriorityQueue常用接口介绍

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

3.2. 插入/删除/获取优先级最高的元素

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("优先级队列已经为空!!!");
}
else{
System.out.println("优先级队列不为空");
}
}

 3.3 PriorityQueue构建大根堆

创建大根堆,只需要构造一个比较器,重写Compare方法就可以了

//构造比较器:
class IntCmp implements Comparator<Integer>{@Overridepublic 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());}
}

3.4PriorityQueue的扩容方式

优先级队列的扩容说明:

如果容量小于64时,是按照oldCapacity的2倍方式扩容的

如果容量大于等于64,是按照oldCapacity的1.5倍方式扩容的

如果容量超过MAX_ARRAY_SIZE,按照MAX_ARRAY_SIZE来进行扩容 

4.top-k问题 OJ链接

1.前k个建堆,建立大小为k的大根堆,把下标为k的元素与堆顶元素比较

2.如果小于堆顶元素,堆顶元素出堆,把此元素放入堆中,k++,再循环遍历完数组

import java.util.PriorityQueue;
class IntCmp implements Comparator<Integer>{@Overridepublic int compare(Integer o1, Integer o2) {return o2-o1;}
}
class Solution {public int[] smallestK(int[] arr, int k) {int [] tmp = new int [k];if(k == 0){return tmp;}PriorityQueue<Integer> maxHeap = new PriorityQueue<>(new IntCmp());//1,把前K个元素放入堆中for(int i = 0; i<k; i++){maxHeap.offer(arr[i]);}//2.遍历剩下的N-K个元素for(int i = k ; i<arr.length;i++){int val = maxHeap.peek();if(val > arr[i]){maxHeap.poll();maxHeap.offer(arr[i]);}}for(int i = 0; i< k; i++){tmp[i] = maxHeap.poll();}return tmp;}
}

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

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

相关文章

微软Copilot+ PC:Phi-Silica

大模型技术论文不断&#xff0c;每个月总会新增上千篇。本专栏精选论文重点解读&#xff0c;主题还是围绕着行业实践和工程量产。若在某个环节出现卡点&#xff0c;可以回到大模型必备腔调重新阅读。而最新科技&#xff08;Mamba&#xff0c;xLSTM,KAN&#xff09;则提供了大模…

C++入门:从C语言到C++的过渡(2)

目录 1.缺省参数 1.1缺省参数的概念 1.2缺省参数的分类及使用 1.3注意点 2.函数重载 2.1函数重载的定义 2.2函数重载的情况分类 2.3注意 2.4函数名修饰规则 3.引用 3.1引用的概念 3.2注意事项 3.3常引用 4.4引用的使用场景 4.4.1作为函数的参数 4.4.2做函数返回…

【学习笔记】Windows GDI绘图目录

题外话 不知几时开始&#xff0c;觉得学习过程中将内容记录下来&#xff0c;有助于加强记忆&#xff0c;还方便后续查找&#xff0c;顺便帮助有需要的人&#xff0c;更有来自您阅读、点赞、收藏和评论时给我带来的动力与兴奋。 目录 【学习笔记】Windows GDI绘图(一)图形概述…

B站大数据分享视频创作300天100+原创内容4000+粉

以今年五一作为一个里程碑参考点&#xff0c;给明年一个可以比较的数据。 我正经发力创作是2023.06.17 (前面几个视频是试水)&#xff0c;300天不到一年时间 创作了100原创数据相关视频&#xff0c;创作频率应该很高了&#xff0c;收获了下面几个数字&#xff0c;审视自身&…

如何建设高效的外贸自建站?

建设高效的外贸自建站&#xff0c;首先要从明确目标和受众开始。了解你的目标市场和潜在客户是关键&#xff0c;这样你可以有针对性地进行设计和内容创作。站点的设计应该简洁明了&#xff0c;导航要方便&#xff0c;确保访客户能够快速找到所需的信息。 而内容是网站的核心。…

Java基础的语法---String

Java的String类是不可变的&#xff0c;意味着一旦创建&#xff0c;其值就不能被改变。String类提供了丰富的API来操作字符串。 以下是一些常用的方法&#xff1a; 构造方法&#xff1a; 有以下几种常见的&#xff1a; public class stringlearn {public static void main(S…

ATmega328P加硬件看门狗MAX824L看门狗

void Reversewdt(){ //硬件喂狗&#xff0c;11PIN接MAX824L芯片WDIif (digitalRead(11) HIGH) {digitalWrite(11, LOW); //低电平} else {digitalWrite(11, HIGH); //高电平 }loop增加喂狗调用 void loop() { …… Reversewdt();//喂狗 }

从0到1!得物如何打造通用大模型训练和推理平台

1.背景 近期&#xff0c;GPT大模型的发布给自然语言处理&#xff08;NLP&#xff09;领域带来了令人震撼的体验。随着这一事件的发生&#xff0c;一系列开源大模型也迅速崛起。依据一些评估机构的评估&#xff0c;这些开源模型大模型的表现也相当不错。一些大模型的评测情况可…

佩戴安全头盔监测识别摄像机

佩戴安全头盔是重要的安全措施&#xff0c;尤其在工地、建筑工程和工业生产等领域&#xff0c;安全头盔的佩戴对于工人的生命安全至关重要。为了更好地管理和监控佩戴安全头盔的情况&#xff0c;监测识别摄像机成为了一项重要的工具。监测识别摄像机可以通过智能技术监测并记录…

JavaScript-数组的增删改查

数组的操作一共有四种&#xff1a; 查询数组数据修改数组中元素的值数组添加新的数据删除数组中的元素 数组的初始化 有些编程语言的数组初始化是用{}包着的&#xff0c;而JS的数组初始化用[] let num[2,6,1,77,52,25,7]; 数组的查询 想要具体查询数组中的某个元素 可以用数…

项目9-网页聊天室7(消息传输模块之解决之前存在的问题:获取最后一条消息)

1.服务器中转的原因 IPV4不够用 &#xff08;1&#xff09;使用服务器中转,最大原因, 就是 NAT 背景下,两个内网的设备无法直接进行通信(不在同一个局域网内) &#xff08;2&#xff09;另外一个原因,通过服务器中转,是更容易在服务器这里记录历史消息随时方便咱们来查询历史记…

C语言实现Hash Map(2):Map代码实现详解

在上一节C语言实现Hash Map(1)&#xff1a;Map基础知识入门中&#xff0c;我们介绍了Map的基础概念和在C中的用法。但我写这两篇文章的目的是&#xff0c;能够在C语言中实现这样的一个数据结构&#xff0c;毕竟有时我们的项目中可能会用到Map&#xff0c;但是C语言库中并没有提…

C语言学习笔记之指针(一)

目录 什么是指针&#xff1f; 指针和指针类型 指针的类型 指针类型的意义 指针-整数 指针的解引用 指针 - 指针 指针的关系运算 野指针 什么是野指针&#xff1f; 野指针的成因 如何规避野指针&#xff1f; 二级指针 什么是指针&#xff1f; 在介绍指针之前&#…

第十五届“北斗杯”全国青少年空天科技体验与创新大赛安徽赛区阜阳市解读会议

5月19日&#xff0c;第十五届“北斗杯”全国青少年空天科技体验与创新大赛安徽赛区阜阳解读活动在阜阳市图书馆隆重举行。共青团阜阳市委员会学少部副部长丁晓龙、阜阳市师范大学物理系副主任黄银生教授、安徽科技报阜阳站站长李伟、市人工智能学会秘书长郭广泽、“北斗杯”安徽…

【开发 | 环境配置】解决 VSCode 编写 eBPF 程序找不到头文件

问题描述&#xff1a; 在使用 vscode 编写 eBPF 程序时&#xff0c;如果不做一些头文件定位的操作&#xff0c;默认情况下头文件总是带有“红色下划线”&#xff0c;并且大部分的变量不会有提示与补全。 在编写代码文件较小时&#xff08;或者功能需求小时&#xff09;并不会…

开放式耳机什么牌子好用?五大高分力作安利,不容错过!

​开放式耳机如今备受瞩目&#xff0c;其独特的不入耳设计不仅避免了长时间佩戴对耳道的压力&#xff0c;还维护了耳朵的卫生健康&#xff0c;特别受运动爱好者和耳机发烧友青睐。然而&#xff0c;市场上的开放式耳机品质良莠不齐&#xff0c;让许多消费者在选择时陷入困惑。作…

嵌入式全栈开发学习笔记---C语言笔试复习大全20

目录 指针数组 数组指针 指针和二维数组 通过指针访问二维数组 通过数组指针访问二维数组 用指针表示二维数组并访问 地址等级 0级地址&#xff1a; 一级地址&#xff1a; 二级地址&#xff1a; 三级地址&#xff1a; 总结 指针的指针 命令行参数 上一篇复习了指…

路由_传递params参数和query参数

传递params参数 传递params参数可以直接在路径后面加上参数&#xff1a; 上述就是在路径变化的时候传过去三个值分别为哈哈、嘿嘿、呵呵的参数 但是这样的话会被认为三个参数是路径的一部分&#xff0c;计算机没有办法区分哪些是路径哪些是参数&#xff0c;所以首先要在这条路…

React useState数组新增和删除项

在React中&#xff0c;我们可以使用useState钩子来管理组件的状态&#xff0c;其中包括数组。如何在React函数组件中对数组进行增加和删除项的操作&#xff1f; 新增项时&#xff1a;我们可以对原数组进行解构&#xff0c;并把新增项一起加入到新数组&#xff1b; 删除项时&…

LeetCode 264 —— 丑数 II

阅读目录 1. 题目2. 解题思路3. 代码实现 1. 题目 2. 解题思路 第一个丑数是 1 1 1&#xff0c;由于丑数的质因子只包含 2 、 3 、 5 2、3、5 2、3、5&#xff0c;所以后面的丑数肯定是前面的丑数分别乘以 2 、 3 、 5 2、3、5 2、3、5 后得到的数字。 这样&#xff0c;我…