优先级队列(堆)

news/2025/1/31 9:34:51/

📖1.优先级队列

🌈队列是一种先进先出(FIFO)的数据结构,但有些情况下,操作的数据可能带有优先级,一般出队列时,可能需要优先级高的元素先出队列;在这种情况下,数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象。这种数据结构就是优先级队列(Priority Queue)

🔥2.堆的模拟实现

优先级队列的实现底层使用了堆的数据结构,首先我们来了解一下堆

2.1 什么是堆

1️⃣小堆:按完全二叉树的顺序存储方式存储在一个一维数组,满足:Ki <=  K2i + 1 且 Ki <= K2i + 2父亲节点大于孩子节点)根节点最小的堆叫做最小堆或小根堆。

5f16716957fc470c8eaf48786007b02c.png

2️⃣大堆:按完全二叉树的顺序存储方式存储在一个一维数组,满足:Ki >=  K2i + 1 且 Ki >= K2i + 2孩子节点大于父亲节点)将根节点最大的堆叫做最大堆或大根堆

df0feea6990b474fbf5cdc5464d06e05.png

 3️⃣堆的性质:堆中某个节点的值总是不大于或不小于其父节点的值; 堆总是一棵完全二叉树(采用顺序方式存储)

2.2 堆向下调整及创建

1️⃣堆向下调整及创建 :对于一个集合的数据,我们需要把它创建成一个堆——大堆或者小堆

1. 让parent标记需要调整的节点,child标记parent的左孩子(注意:parent如果有孩子一定先是有左孩子)

2. 如果parent的左孩子存在,即:child < size,进行以下操作,直到parent的左孩子不存在

parent右孩子是否存在,存在找到左右孩子中最小的孩子,让child进行标

将parent与较小的孩子child比较,如果:
parent大于较小的孩子child,调整结束 
否则:交换parent与较小的孩子child,交换完成之后,parent中大的元素向下移动,可能导致子树不满足对的性质,因此需要继续向下调整,即parent = child;child = parent*2+1; 然后继续2。
 

我们以大堆为例:从一棵树的最后一棵子树开始,依次调整成为大根堆——左右孩子节点比较大小,大的与其根点交换,直到成为大堆

4fb4688f37664d4f9ceba9c400cc38f1.png

public class TestHeap {public int[] elem;public int usedSize;public TestHeap() {this.elem = new int[10];}public void initElem(int[] array) {for (int i = 0; i < array.length; i++) {elem[i] = array[i];usedSize++;}}/*** 时间复杂度:O(N) *///堆的创建public void createHeap() {for(int parent = (usedSize-1-1)/2;parent >= 0; parent--) {//从长度-1开始,第一个父亲结点为{(长度-1)-1}/2shiftDown(parent,usedSize);}}/*** 父亲下标* 每棵树的结束下标* @param parent* @param len*///向下调整—— 时间复杂度:logNpublic void shiftDown(int parent,int len) {int child = 2*parent + 1;//定义孩子节点//最起码 要有左孩子while(child < len) {//一共usedSize个数,孩子节点小于总长度//一定是有右孩子的情况下if(child+1 < len && elem[child] < elem[child+1]) {//child+1 < len防止越界child++;//右孩子节点大的话,child++就不变成了右孩子}//此时,child下标一定是左右孩子最大值的下标if(elem[child] > elem[parent]) {//当孩子节点大于父亲节点,使用第三方将最大和最小值交换int tmp = elem[child];elem[child] = elem[parent];elem[parent] = tmp;//此时完成了交换,但是有一个问题完成交换之后的孩子节点与孩子的孩子节点此时的大小顺序又被打乱//所以让父亲节点等于孩子节点,孩子节点等于孩子的孩子节点继续比较大小parent = child;child = 2*parent + 1;}else {break;}}}
}

 2️⃣建堆的时间复杂度——O(N)

8df86bf30fe34b74811880a7caa6c317.png

2.3 堆的插入与删除

1️⃣堆的插入:堆的插入使用向上调整——时间复杂度O(N*logN)

c————孩子节点           p————父亲节点

cf42abb986564c4f8da9a4c00640b1bb.png

    //堆的插入:堆的插入使用向上调整public void offer(int val) {if(isFull()) {//扩容elem = Arrays.copyOf(elem,2*elem.length);}elem[usedSize++] = val;最大元素下标为插入元素,元素个数+1————11//向上调整shiftUp(usedSize-1);//10————下标为10}public boolean isFull() {return usedSize == elem.length;}//向上调整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;}}}

5bf0877aea364dc38d0c556c5fb8f629.png

 2️⃣堆的删除——删除的一定是堆顶元素

 第一步:让堆顶元素和堆中最后一个元素交换                第二步:这时候有效数据减少一个,这时候只需要调整0下标这棵树

 第三步: 使用向下调整——时间复杂度:logN

fc02d289c99c4b4d8f11d8bb5d43086b.png

    //堆的删除——删除的一定是堆顶元素//第一步:让堆顶元素和堆中最后一个元素交换    第二步:这时候有效数据减少一个,这时候只需要调整0下标这棵树     第三步: 使用向下调整public void pop() {if(isEmpty()) {return;}swap(elem,0,usedSize-1);usedSize--;//交换堆顶元素和堆中最后一个元素,此时数据减少一个shiftDown(0,usedSize);//向下调整}public boolean isEmpty() {return usedSize == 0;}//交换public void swap(int[] array,int i,int j) {int tmp = array[i];array[i] = array[j];array[j] = tmp;}//向下调整 时间复杂度——logNprivate void shiftDown(int parent,int len) {int child = 2*parent + 1;//定义孩子节点//最起码 要有左孩子while (child < len) {//一共usedSize个数,孩子节点小于总长度//一定是有右孩子的情况下if(child+1 < len && elem[child] < elem[child+1]) {//child+1 < len防止越界child++;//右孩子节点大的话,child++就不变成了右孩子}//child下标 一定是左右孩子 最大值的下标if(elem[child] > elem[parent]) {//当孩子节点大于父亲节点,使用第三方将最大和最小值交换int tmp = elem[child];elem[child] = elem[parent];elem[parent] = tmp;//此时完成了交换,但是有一个问题完成交换之后的孩子节点与孩子的孩子节点此时的大小顺序又被打乱//所以让父亲节点等于孩子节点,孩子节点等于孩子的孩子节点继续比较大小parent = child;child = 2*parent+1;}else {break;}}}

🏹3.PriorityQueue常用接口

3.1 PriorityQueue的使用

1️⃣使用时必须导入PriorityQueue所在的包,即:

importjava.util.PriorityQueue;

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

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

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

5️⃣插入和删除元素的时间复杂度为 O(logN)

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

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

3.2 构造方法

 任何一个类,都需要从构造方法出发来理解,下面我们来看PriorityQueue的构造方法

//创建一个空的优先级队列,默认容量是11且默认没有比较器
PriorityQueue()     //创建一个初始容量为initialCapacity的优先级队列,注意:initialCapacity不能小于1,否则会抛IllegalArgumentException异常        
PriorityQueue(int initialCapacity)//用一个集合来创建优先级队列
PriorityQueue(Collection<?extends E> c)          

🔎4.最小的K个数【leetcod】

🔎leetcod题目:设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可

👉题目链接:最小K个数

💡做题思路:建立小根堆

    //最小K个数//设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可。public int[] smallestK(int[] arr, int k) {int[] ret = new int[k];if(arr == null || k == 0) {return ret;}//O(N*logN)————向上调整Queue<Integer> minHeap = new PriorityQueue<>(arr.length);for (int x:arr) {minHeap.offer(x);//遍历小根堆,放入小根堆中}//O(k*logN)————弹k次,每次向下调整for (int i = 0; i < k; i++) {ret[i] = minHeap.poll();}return ret;}

🌈5.Top-k问题

✨TOP-K问题:即求数据集合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大

💡做题思路:1️⃣用数据集合中前K个元素来建堆

前k个最大的元素,则建小堆                    前k个最小的元素,则建大堆

2️⃣用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素

将剩余N-K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素。

🥇例求最大的K个值:

c6b4d77c1a124dc5817c5bfe0ac94927.png

    /*** 前K个最大的元素   时间复杂度:N*logK* @param arr* @param k* @return*/public int[] maxK2(int[] arr, int k) {int[] ret = new int[k];if(arr == null || k == 0) {return ret;}Queue<Integer> minHeap = new PriorityQueue<>(k);//建立大小为k的小根堆//1.遍历数组的前K个  放到堆当中for (int i = 0; i < k; i++) {minHeap.offer(arr[i]);//前K个小根堆}//2.遍历剩下的数据,每次和堆顶元素进行比较//堆顶元素  小的时候,就出堆for (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;}

 

 

 

 

 

 

 

 

 

 

 

 

 

 


http://www.ppmy.cn/news/94737.html

相关文章

【SpringCloud】Gateway网关

文章目录 1、网关的作用2、搭建网关服务3、路由断言4、GatewayFilter5、全局过滤器6、过滤器的执行顺序7、限流过滤器8、跨域问题处理 1、网关的作用 服务就像一个景点&#xff0c;如果人人可以访问&#xff0c;不管是游客还是搞破坏的人都放进来&#xff0c;那一定出事。由此…

数据库基础——3.SQL概述及规范

这篇文章我们来讲一下SQL概述和使用规范 目录 1.SQL概述 1.1SQL背景 1.2 SQL语言排行榜 1.3 SQL分类 2.SQL规则与规范 2.1基本规则 2.2 SQL大小写规范 &#xff08;建议遵守&#xff09; 2.3 注 释 2.4 命名规则&#xff08;暂时了解&#xff09; 2.5 数据导入指令 1…

LAMP平台搭建

文章目录 LAMP概述安装apache安装mysql安装php LAMP概述 LAMP架构是目前成熟的企业网站应用模式之一&#xff0c;指的是协同工作的一整套系统和相关软件&#xff0c;能够提供动态Web站点服务及其应用开发环境。LAMP是一个缩写词&#xff0c;具体包括Linux操作系统、Apache网站…

华为OD机试真题B卷 Java 实现【小朋友排队】

一、题目描述 小明今年升学到了小学1年级&#xff0c;来到新班级后&#xff0c;发现其他小朋友身高参差不齐&#xff0c;然后就想基于每个小朋友和自己的身高差&#xff0c;对他们进行排序&#xff0c;请帮他实现排序。 二、输入描述 第一行为正整数h和n。 0 < h < 2…

编译原理之词法分析实验(附完整C/C++代码与总结)

一、实验内容 通过完成词法分析程序&#xff0c;了解词法分析的过程。编制一个读单词程序&#xff0c;对PL/0语言进行词法分析&#xff0c;把输入的字符串形式的源程序分割成一个个单词符号&#xff0c;即基本保留字、标识符、常数、运算符、分界符五大类。 对PL/0语言进行词法…

华为OD机试之不含101的整数(Java源码)

不含101的数 题目描述 小明在学习二进制时&#xff0c;发现了一类不含 101的数&#xff0c;也就是&#xff1a; 将数字用二进制表示&#xff0c;不能出现 101 。 现在给定一个整数区间 [l,r] &#xff0c;请问这个区间包含了多少个二进制不含 101 的整数&#xff1f; 输入描述…

CSDN 编程竞赛五十五期题解

竞赛总览 CSDN 编程竞赛五十五期&#xff1a;比赛详情 (csdn.net) 吐槽&#xff1a;填空题参考答案错误&#xff0c;赛后竟然没有修正…… 竞赛题解 题目1、取石子 将n堆石子摆成一排。游戏规则&#xff1a;两人轮流从最左或最右的一堆中取出若干颗石子&#xff08;可以将…

【C++系列P3】‘类与对象‘-三部曲——[基础知识](1/3)

前言 大家好吖&#xff0c;欢迎来到 YY 滴 C系列 &#xff0c;热烈欢迎&#xff01; 【 类与对象-三部曲】的大纲主要内容如下&#xff1a; 如标题所示&#xff0c;本章是【 类与对象-三部曲】三章中的第一章节——基础知识章节&#xff0c;主要内容如下&#xff1a; 目录 一.…