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

[数据结构]优先级队列

1.优先级队列

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

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

2..1 堆的概念

如果有一个集合 , 把集合中的所有元素 按照完全二叉树的顺序存储方式 存储在一个一维数组中

  • 将根节点最大的堆叫做大根堆
  • 将根节点最小的堆叫做小根堆

[总结] :

  • 堆中某个结点的值 总是 不大于或者不小于其父亲结点的值
  • 堆总是一颗完全二叉树

以小根堆为例

逻辑结构 :

存储结构 :


2.2 堆的创建

2.2.1 堆的向下调整

① 创建一个堆类
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++){this.elem[i] = array[i];this.usedSize++;}
}
③ 向下调整创建堆
public void createHeap(){for(int parent = (usedSize-1)/2;parent>=0;parent--){siftDown(parent,this.usedSize);}
}private void siftDown(int parent, int usedSize) {int child = parent*2+1;while(child<usedSize){if(child+1<usedSize&&elem[child]<elem[child+1])child++;//让child指向较大值if(elem[parent]<elem[child]){swap(elem,parent,child);//交换parent = child;//继续向下调整child = child*2+1;}else {break;}}}
public void swap(int[] elem,int i,int j ){int tmp = elem[i];elem[i] = elem[j];elem[j] = tmp;
}

[注意]:

createHeap() 逻辑:堆化过程从最后一个非叶子节点开始,依次对每个节点执行siftDown,直到根节点(索引 0)
siftDown(int parent, int usedSize) 核心逻辑:对指定父节点,找到其左右子节点中的最大值,若父节点小于该最大值,则交换两者,并继续对新的子节点执行下沉操作,直到满足大根堆性质
  1. 计算左子节点索引(parent*2+1)。
  2. 比较左右子节点,让child指向较大的子节点(若右子节点存在)。
  3. 若父节点小于child指向的子节点,交换两者,并更新parentchild继续下沉。
  4. 若父节点 ≥ 子节点,说明当前节点已满足堆性质,退出循环

2.2.2 堆的向上调整

向上调整建堆
public void swap(int[] elem,int i,int j ){int tmp = elem[i];elem[i] = elem[j];elem[j] = tmp;}public void siftUp(int child, int usedSize){int parent = (child-1)/2;while (parent>=0) {if (elem[child] > elem[parent]) {swap(elem, parent, child);child = parent;parent = (child - 1) / 2;}else {break;}}}

[注意]:

维度

向上调整(siftUp)

向下调整(siftDown)

操作方向

从下往上(子 → 父)

从上往下(父 → 子)

触发场景

插入元素(末尾新增元素)

删除堆顶元素、堆初始化

比较对象

当前节点与父节点

父节点与左右子节点中的最优者

终止条件

到达堆顶或满足堆性质

子节点超出范围或满足堆性质

时间复杂度

O (log n)(堆的高度)

O (log n)(堆的高度)

2.3.1 插入操作(上浮 Adjust Up)

步骤 :

尾插 : 将新元素加入数组的末尾(对应二叉树的最后一个位置) , 保证其结构是一颗完全二叉树

向上调整 : 比较新元素与其父亲结点的值 , 若不满足堆属性 , 则交换二者 , 重复此过程直到新元素称为堆顶

代码 :
public void swap(int[] elem,int i,int j ){int tmp = elem[i];elem[i] = elem[j];elem[j] = tmp;}public void siftUp(int child, int usedSize){int parent = (child-1)/2;while (parent>=0) {if (elem[child] > elem[parent]) {swap(elem, parent, child);child = parent;parent = (child - 1) / 2;}else {break;}}}

public boolean isFull(){return usedSize == elem.length;}
public void offer(int val){//在尾部增加元素if(isFull()){elem = Arrays.copyOf(elem,elem.length*2);}elem[usedSize] = val;//在最末尾添加元素siftUp(elem[usedSize],usedSize);//向上调整 先调整再++; 如果先++,则可能会超出范围usedSize++;
}

2.3.2 删除堆顶操作(下沉 Adjust Down)

步骤:

替换堆顶 : 将数组尾元素与堆顶元素位置互换 , 然后 usedSize--

向下调整 : 对指定父节点,找到其左右子节点中的最大值,若父节点小于该最大值,则交换两者,并继续对新的子节点执行下沉操作,直到满足大根堆性质

代码 :
private void siftDown(int parent, int usedSize) {int child = parent*2+1;while(child<usedSize){if(child+1<usedSize&&elem[child]<elem[child+1])child++;//让child指向较大值if(elem[parent]<elem[child]){//创建大根堆swap(elem,parent,child);//交换parent = child;child = child*2+1;}else {break;}}}
public void swap(int[] elem,int i,int j ){int tmp = elem[i];elem[i] = elem[j];elem[j] = tmp;
}

public int  poll(){//删除顶层元素if(isFull()){return -1;}int val = elem[0];swap(elem,0,usedSize-1);//交换 0下标元素 和 最后一个元素siftDown(0,usedSize-1);//向下调整 不调整此时最后一个元素 , 因为该元素是要删除的元素,直接让usedSize--就好了usedSize--;return val;
}
public int peek() {//获取顶层元素if(isEmpty()) {return -1;}return elem[0];
}

2.3.3 基于大根堆 , 完成升序排列

public void heepSort(){//基于大根堆 , 完成升序排列int end = usedSize-1;while(end>0){swap(elem,0,end);//基于大根堆 , 顶层为最大值 , 将最大值放在末尾处 , 下一次循环将较大者放到end-1位置 , end位置是排序好的元素siftUp(0,end);//注意这个end是堆的大小 , 已经减过1了end--;//指向下一次末尾序号}
}

3.常用接口介绍

3.1 PriorityQueue 的特性

Java 集合框架中提供了 PriorityQueue 和 PriorityBlockingQueue 两种类型的优先级队列

  • PriorityQueue 是线程不安全的
  • PriorityBlockingQueue 是线程安全的

注意:

  • 使用时需要导入包 : import java.util.PriorityQueue;
  • PriorityQueue 中放入的元素必须能够比较大小 , 不能插入无法比较大小的对象 , 否则抛出 ClassCastException 异常
  • 不能插入 null 对象 , 否则抛出 NullPointerException
  • 没有容量限制 , 可以插入任意多个元素 , 其内部可以自动扩容
  • PriorityQueue 底层使用了堆数据结构
  • PriorityQueue 默认情况下是小堆--即每次获取到的元素都是最小元素

3.2 优先级队列的构造

代码 :
public static void main(String[] args) {//创建一个空的优先级队列,默认是小根堆排序,可以在括号里指定初始大小PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();//默认是小根堆priorityQueue.offer(44);priorityQueue.offer(23);priorityQueue.offer(4);System.out.println(priorityQueue);//[4, 44, 23]//用比较器创建一个优先级队列,是以大根堆排序PriorityQueue<Integer> priorityQueuebig = new PriorityQueue<>(new IntCmp());//利用比较器创建出来的是大根堆priorityQueuebig.offer(44);priorityQueuebig.offer(23);priorityQueuebig.offer(4);System.out.println(priorityQueuebig);//[44, 23, 4]//用一个集合来创建优先级队列List<Integer> list = Arrays.asList(3, 1, 4, 1, 5);PriorityQueue<Integer> priorityQueue1 = new PriorityQueue<>(list);System.out.println(priorityQueue1);//[1, 1, 4, 3, 5]
}
class IntCmp  implements Comparator<Integer> {@Overridepublic int compare(Integer o1, Integer o2) {return o2.compareTo(o1);}

3.3 常用方法

3.3.1 基本操作方法

方法

功能描述

add(E e)

向队列中添加元素,成功返回true

,如果元素为null

或不满足比较规则会抛出异常

offer(E e)

向队列中添加元素,成功返回true

,失败返回false

(更推荐使用,不会抛出异常)

poll()

移除并返回队列的头部元素(优先级最高的元素),队列为空时返回null

remove()

移除并返回队列的头部元素,队列为空时抛出NoSuchElementException

clear()

清空队列中的所有元素

3.3.2 元素查询方法

方法

功能描述

peek()

返回队列的头部元素(优先级最高的元素),但不移除它,队列为空时返回null

element()

返回队列的头部元素,队列为空时抛出NoSuchElementException

contains(Object o)

判断队列中是否包含指定元素,返回boolean

size()

返回队列中元素的数量

isEmpty()

判断队列是否为空,返回boolean

3.3.3 批量操作与迭代

方法

功能描述

addAll(Collection<? extends E> c)

将集合中的所有元素添加到队列中

removeAll(Collection<?> c)

移除队列中与集合交集的元素

retainAll(Collection<?> c)

保留队列中与集合交集的元素,移除其他元素

iterator()

返回迭代器,用于遍历队列元素(注意:迭代顺序不保证优先级顺序

代码 :

import java.util.Arrays;
import java.util.PriorityQueue;public class PriorityQueueMethods {public static void main(String[] args) {// 创建一个小顶堆PriorityQueue<Integer> pq = new PriorityQueue<>();// 添加元素pq.offer(5);pq.offer(2);pq.offer(8);pq.offer(1);System.out.println("添加元素后: " + pq);  // 内部存储:[1, 2, 8, 5]// 查看头部元素System.out.println("头部元素(peek): " + pq.peek());  // 1// 移除并返回头部元素System.out.println("移除的元素(poll): " + pq.poll());  // 1System.out.println("poll后队列: " + pq);  // [2, 5, 8]// 判断是否包含元素System.out.println("是否包含5: " + pq.contains(5));  // true// 批量添加元素pq.addAll(Arrays.asList(3, 6));System.out.println("批量添加后: " + pq);  // [2, 3, 8, 5, 6]// 遍历元素(注意:迭代顺序不是优先级顺序)System.out.print("迭代器遍历: ");for (int num : pq) {System.out.print(num + " ");  // 可能输出:2 3 8 5 6}System.out.println();// 按优先级顺序输出所有元素System.out.print("按优先级出队: ");while (!pq.isEmpty()) {System.out.print(pq.poll() + " ");  // 2 3 5 6 8}}
}

4. 应用

Top K 问题(求前 K 个最大 / 小元素)

堆是解决 Top K 问题的高效方案,时间复杂度为 O (n log K),适合处理海量数据。

方法 ①
public int[] smallestK1(int[] arr, int k) {PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();for(int i = 0;i<arr.length;i++){priorityQueue.offer(arr[i]);}int[] ret = new int[k];for(int i = 0;i<k;i++){ret[i] = priorityQueue.poll();}return ret;
}

方法 ②
public int[] smallestK(int[] arr, int k) {int[] ret = new int[k];if(k == 0||arr == null){return ret;}PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(new IntCmp1());for(int i = 0;i<k;i++){priorityQueue.offer(arr[i]);}for(int i = k;i<arr.length;i++){int tmp = priorityQueue.peek();if(tmp>arr[i]){priorityQueue.poll();priorityQueue.offer(arr[i]);}}for(int i = 0;i<k;i++){ret[i] = priorityQueue.poll();}return ret;}
class IntCmp1 implements Comparator<Integer> {@Overridepublic int compare(Integer o1, Integer o2) {return o2.compareTo(o1);}}

http://www.dtcms.com/a/409918.html

相关文章:

  • ARM内部寄存器
  • Laravel + UniApp AES加密/解密
  • 5G开户时切片配置参数详解
  • 面向新质生产力,职业院校“人工智能”课程教学解决方案
  • wap网站如何做福建外贸网站
  • ElasticSearch-提高篇
  • 第6篇、Flask 表单处理与用户认证完全指南:从零到实战
  • Visual Studio 2013 Update 4 中文版安装步骤(带TFS支持)附安装包​
  • 珠海 网站建设注册安全工程师题库
  • 上手 cpp-httplib:轻量级 C++ HTTP 库的安装与实战指南
  • 突破文档型数据库迁移困境:金仓多模方案破解电子证照系统国产化难题
  • 网站手机客户端开发wordpress制造商单页
  • Net 》》C# 》》try finally 执行顺序
  • 在 Unity 项目中使用 FFMpeg 进行音频转码(WAV 转 MP3)
  • 使用Java将Word文件转换为PNG图片
  • 如何用Fail2ban保护Linux服务器?防止SSH暴力破解教程
  • 开源 C# 快速开发(五)自定义控件--仪表盘
  • 华为FreeClip 2耳夹耳机:让「戴着不摘」成为新的使用习惯
  • 算法继续刷起-2025年09月26日
  • AI笔记在学习与工作中的高效运用
  • QML学习笔记(十四)QML的自定义模块
  • ubuntu一键安装vscode: 使用官方 APT 仓库
  • python做网站的 框架企业邮箱什么样子
  • 学习游戏制作记录(爆炸敌人的制作)
  • 第三方软件登记测试机构:【软件登记测试机构HTML5测试技术】
  • 元宇宙的宠物经济:虚拟宠物的饲养与交易
  • 如何在 CentOS 7 上安装 bzip2-libs-1.0.6-13.el7.x86_64.rpm 文件
  • 做网站的盈利模式北京软件开发公司找和丰软件专业
  • 长视频分析模型 LongVU 论文内容总结与技术架构解析
  • Datawhale25年9月组队学习:llm-preview+Task4:微调大模型