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

【数据结构】PriorityQueue优先队列:基于堆(heap)实现

要理解 PriorityQueue(优先队列),核心是先搞懂「堆(heap)」这个底层数据结构,再看队列如何用堆实现“按优先级排序”

一、先搞懂

PriorityQueue 是干嘛的?

“普通队列”先进先出 (FIFO),谁先来谁先走。
“优先队列” 则不同,它是 谁 “优先级” 高谁先走。

医院挂号队列

  • 普通病人排队看医生(FIFO)
  • 急诊病人优先(Priority)

所以 PriorityQueue 本质上就是:

一种能“随时插入”,但“取出时总是取最高优先级”的结构。

堆(heap)到底是什么?

堆不是“垃圾堆”,而是一种 “有序的完全二叉树”(可以简单理解成“长得很规整、且有大小规则的树”),主要分两种:

  • 大顶堆:树的“根节点”是最大的,且每个父节点都比自己的子节点大(像一个金字塔,顶端是最大的);
  • 小顶堆:树的“根节点”是最小的,且每个父节点都比自己的子节点小(像一个倒金字塔,顶端是最小的)。
堆的核心特点(通俗版):
  1. “规整”:完全二叉树 = 除了最后一层,每一层的节点都排满了;最后一层的节点从左到右依次排列,不跳空(比如3层树,第1层1个节点,第2层2个,第3层先排左边2个,再排右边,不会左边空着先排右边);
  2. “有规矩”:父节点和子节点必须满足“大顶/小顶”规则(比如大顶堆里,爸爸永远比儿子大,不会出现“儿子比爸爸大”的情况);
  3. “好维护”:添加/删除节点后,能快速调整回“规整+有规矩”的状态(这个调整过程叫“上浮”或“下沉”,后面会说)。
堆的可视化(小顶堆例子):
    1  (根节点,最小)/ \3   2  (父节点3>1,2>1,符合规则)/ \ /
4  5 6  (父节点4>3,5>3,6>2,符合规则)

这就是一个小顶堆,顶端永远是最小的元素,且结构规整。

二、PriorityQueue:用堆实现“按优先级出队”

普通队列是「先进先出(FIFO)」——比如排队买奶茶,先到的先买;
但优先队列是「优先级高的先出」——比如医院急诊,不管谁先来,病情重的先救治。

堆就是优先队列的“底层容器”:用大顶堆实现“最大元素优先出队”,用小顶堆实现“最小元素优先出队”(Java的PriorityQueue默认是小顶堆)。

核心逻辑:堆的“顶端”就是“当前优先级最高的元素”

因为堆的规则(大顶堆顶端最大,小顶堆顶端最小),每次要出队时,直接取堆顶元素就行——这是最高效的方式(时间复杂度O(1))。

关键是:添加/删除元素后,如何保持堆的规则? 用两个核心操作:

三、两个关键操作:保证堆的“规矩”不被破坏

假设我们用「小顶堆」实现优先队列(优先出最小元素),看两个常见场景:

1. 新增元素(入队):上浮操作

比如我们往上面的小顶堆里加一个元素「0」,步骤:

  1. 先把新元素放到堆的“最后一个位置”(保证结构是完全二叉树,不破坏“规整”):
        1/ \3   2/ \ / \
    4  5 6  0  (新元素0放在最后)
    
  2. 发现“儿子(0)比爸爸(2)小”,违反小顶堆规则——需要“上浮”:把儿子和爸爸交换位置,直到符合规则:
    • 第一次交换(0和2):
          1/ \3   0  (爸爸2和儿子0交换)/ \ / \
      4  5 6  2
      
    • 再检查“儿子(0)比爷爷(1)小”,继续交换:
          0  (新的根节点,最小元素)/ \3   2/ \ / \
      4  5 6  2
      
    此时堆的规则恢复,新元素0成了堆顶,下次出队就会先出它(符合“最小优先”)。
2. 删除元素(出队):下沉操作

优先队列出队只能出“堆顶元素”(优先级最高的),比如把上面的堆顶「0」删掉,步骤:

  1. 先把堆的“最后一个元素”放到堆顶(填补空位,保证结构是完全二叉树):
        2  (最后一个元素2移到堆顶)/ \3   2/ \ / \
    4  5 6  (原来的最后一个位置空了,结构仍规整)
    
  2. 发现“爸爸(2)比儿子(3和2)中的最小儿子(2)相等,比另一个儿子(3)小”——这里假设儿子中有比爸爸小的,需要“下沉”:把爸爸和最小的儿子交换位置,直到符合规则:
    • 这里堆顶2的两个儿子是3和2,最小的儿子是2,交换后:
          2  (堆顶还是2,和儿子交换后没变化)/ \3   2/ \ / \
      4  5 6
      
    (如果爸爸比儿子大,会一直下沉到合适位置)
  3. 此时堆的规则恢复,新的堆顶是2(当前最小元素),下次出队就出它。

四、总结:PriorityQueue的核心逻辑

  • 底层用「堆」(完全二叉树+大小规则)存储元素,堆顶永远是“当前优先级最高的元素”(小顶堆→最小,大顶堆→最大);
  • 入队:新元素放最后,然后“上浮”到合适位置,保持堆规则;
  • 出队:只出堆顶元素,然后用最后一个元素补位,再“下沉”到合适位置,保持堆规则;
  • 优势:不管入队还是出队,调整堆的时间复杂度都是O(log n)(比普通数组排序快多了),适合需要“动态按优先级取元素”的场景(比如任务调度、Top K问题)。

五、生活化类比

把优先队列想象成「公司的任务池」:

  • 堆 = 任务排序表,规定“优先级高的任务(比如紧急bug)放在最前面”;
  • 入队 = 新增一个任务,会自动按优先级插入到排序表的合适位置(不会乱);
  • 出队 = 每次只拿最紧急的任务(排序表第一个,对应堆顶);
  • 上浮/下沉 = 新增/完成任务后,自动调整排序表,保证最紧急的任务永远在第一个。

六、其他实现方式对比

实现方式插入删除最值是否全局有序特点
有序数组/有序链表O(n)O(1)✅ 是插入慢、取出快
堆(Heap)O(log n)O(log n)❌ 否插入删都快、部分有序(最常用)
http://www.dtcms.com/a/572904.html

相关文章:

  • PCB设计如何防止别人抄板?
  • macOS自定义安装PlatformIO Core
  • VSCode中Copilot的询问、编辑、代理有啥区别?
  • 二重积分器(Double Integrator)
  • APP与小程序分账系统是什么?资金管理新思路,合规高效分账
  • Hudi和Iceberg的Specification规范角度详细比较异同点
  • 临安网站建设杭州低价做网站
  • 肇庆市手机网站建设品牌专业做网站企业
  • 幂等性 VS 分布式锁:分布式系统一致性的两大护法 —— 从原理到实战的深度剖析
  • 初识DDD架构
  • 一次redis内存泄露故障分析
  • 计算机网络自顶向下方法32——网络层 网络层概述 转发和路由选择,数据平面和控制平面(传统方法,SDN方法) 网络服务模型
  • 深入理解MySQL_3 I/O成本
  • 哪个网站可以做验证码兼职gom传奇网站建设
  • 做网站一年能赚多少钱没有备案的网站怎么挂广告
  • vscode-ssh无法进入docker问题解决
  • iOS 应用网络权限弹窗的问题及解决方案
  • 使用 FastAPI 异步动态读取 Nacos 配置
  • 怀远做网站电话网站建设期末作业要求
  • Arbess零基础学习 - 使用Arbess+GitLab实现PHP项目构建/主机部署
  • CS144 Lab:Lab0
  • 总结做产品开发的一些通病
  • 稳定币市场格局重构:分发权正在成为新的护城河!
  • 【C语言】深入理解指针(二)
  • C++:模板的灵魂——从编译期推导到元编程的演化史
  • 开发网站用得最多的是什么语言电子商务网站开发课程
  • 顺德中小企业网站建设网站销户说明
  • Python3 面向对象编程详解
  • 【 SLF4J + Logback】日志使用方法+技巧介绍+项目示例(SpringBoot)
  • 重构可见性:IT资产管理的下一次觉醒