【UCB CS 61B SP24】Lecture 21: Data Structures 5: Priority Queues and Heaps 学习笔记
本文介绍了优先队列与堆,分析了最小堆的插入与删除过程,并用 Java 实现了一个通用类型的最小堆。
1. 优先队列
1.1 介绍
优先队列是一种抽象数据类型,其元素按照优先级顺序被处理。不同于普通队列的先进先出(FIFO),优先队列每次取出优先级最高(或最低)的元素。
在 Java 中,PriorityQueue 是基于堆(Heap)实现的,默认使用自然排序(最小堆),也可以通过自定义 Comparator 调整优先级顺序:
package CS61B.Lecture21;import java.util.Collections;
import java.util.List;
import java.util.PriorityQueue;public class PriorityQueueDemo {public static void main(String[] args) {PriorityQueue<Integer> Q = new PriorityQueue<>(); // 默认为小根堆PriorityQueue<Integer> RQ = new PriorityQueue<>(Collections.reverseOrder()); // 大根堆for (int i = 5; i > 0; i--)Q.add(i); // 也可以用 offer() 方法System.out.println(Q); // [1, 2, 4, 5, 3],直接输出不一定有序while (!Q.isEmpty())System.out.print(Q.poll() + " "); // 也可以用 remove() 方法,输出 1 2 3 4 5System.out.println();RQ.addAll(List.of(8, 6, 7, 10, 9));while (RQ.peek() != null)System.out.print(RQ.remove() + " "); // 10 9 8 7 6System.out.println();}
}
堆是一种完全二叉树,完全二叉树是指除了最后一层外,其他层的节点都必须是满的(所有可能的节点都存在),且最后一层的节点必须尽可能靠左排列。最后一层可以不满,但所有节点必须集中在左侧,中间不能有空缺。完全二叉树的高度为 ⌊ l o g n ⌋ + 1 \lfloor log n\rfloor + 1 ⌊logn⌋+1,在相同节点数的二叉树中高度最小。
堆又分为最小堆和最大堆:
- 最小堆:父节点的值 ≤ 子节点的值,堆顶元素为最小值;
- 最大堆:父节点的值 ≥ 子节点的值,堆顶元素为最大值。
堆通常用数组实现,利用完全二叉树的性质能够简化父子节点索引计算,优先级最高的元素一定在堆顶,也就是索引为 0 的节点:
- 父节点索引:
(i - 1) / 2 - 左子节点索引:
2 * i + 1 - 右子节点索引:
2 * i + 2
下图是一个最小堆的示例:

1.2 插入
我们以最小堆为例,插入步骤如下:
- 将新元素插入到堆的末尾(数组的最后一个位置)。
- 上浮(Swin)调整:从新元素的位置开始,与其父节点比较。
- 如果新元素的值小于父节点,则交换两者的位置。
- 重复此过程,直到新元素到达根节点,或满足父节点 ≤ 当前节点的条件。
我们用图片来展示一下最小堆的插入与上浮过程,首先在末尾插入节点 3,然后判断与其父节点的大小关系,小于父节点 5,因此与父节点交换位置,然后继续判断还是小于其父节点 5,和父节点交换,最后判断该节点大于等于其父节点 1,完成上浮调整:

1.3 删除
还是以最小堆为例,删除步骤如下:
- 移除堆顶元素(最小值),将其返回。
- 将堆的最后一个元素移动到堆顶(填补空缺,同时保持完全二叉树的状态)。
- 下沉(Sink)调整:从堆顶开始,与左右子节点中的较小者比较。
- 如果当前节点的值大于较小子节点,则交换两者的位置。
- 重复此过程,直到当前节点到达叶子节点,或满足父节点 ≤ 任意子节点的条件。
同样用图片展示一下最小堆的删除与下沉过程,我们在前面的最小堆上删除堆顶元素(最小值),删除后先将最后一个元素 5 移动到堆顶,然后进行下沉调整,判断 5 大于较小子节点 1,因此与 1 进行交换,接着继续判断还是小于较小子节点 3,因此与 3 进行交换,交换后已经为叶子节点,完成下沉调整:

可以看到堆的上浮与下沉操作最坏情况下就是遍历树的高度,因此添加和删除操作的时间复杂度最坏情况下均为 O ( l o g n ) O(log n) O(logn)。优先队列与其他数据结构的时间复杂度对比如下:
| 数据结构 | 插入 | 查看最值 | 删除最值 | 空间复杂度 | 适用场景 |
|---|---|---|---|---|---|
| 有序数组 | O ( n ) O(n) O(n) | O ( 1 ) O(1) O(1) | O ( n ) O(n) O(n) | O ( n ) O(n) O(n) | 静态数据、极少插入 |
| 平衡搜索树 | O ( l o g n ) O(log n) O(logn) | O ( l o g n ) O(log n) O(logn) | O ( l o g n ) O(log n) O(logn) | O ( n ) O(n) O(n) | 需范围查询或全局有序 |
| 哈希表 | O ( 1 ) O(1) O(1) | O ( n ) O(n) O(n) | O ( n ) O(n) O(n) | O ( n ) O(n) O(n) | 快速查找某个键、无顺序要求 |
| 二叉堆 | O ( l o g n ) O(log n) O(logn) | O ( 1 ) O(1) O(1) | O ( l o g n ) O(log n) O(logn) | O ( n ) O(n) O(n) | 动态数据、频繁插入和提取 |
2. Java 实现最小堆
相信看完上面的讲解也能感觉到堆的实现并不复杂,Java 实现最小堆代码如下:
package CS61B.Lecture21;public class MinHeap<T extends Comparable<T>> {private T[] heap;private int size;private static final int DEFAULT_CAPACITY = 2; // 默认容量public MinHeap() {this(DEFAULT_CAPACITY);}public MinHeap(int capacity) {heap = (T[]) new Comparable[capacity];size = 0;}/** 核心操作:添加 */public void add(T x) {if (size >= heap.length) resize();heap[size] = x;swim(size); // 从末尾开始上浮size++;}/** 核心操作:删除并返回堆顶元素(最小值) */public T remove() {if (size == 0) return null;T root = heap[0];heap[0] = heap[--size];heap[size] = null; // 清除引用,防止内存泄漏sink(0); // 从根节点开始下沉return root;}/** 核心操作:返回堆顶元素(最小值) */public T peek() {return heap[0];}/** 获取大小 */public int size() {return size;}/** 是否为空 */public boolean isEmpty() {return size == 0;}/** 核心操作:上浮 */private void swim(int idx) {int parent = idx - 1 >> 1;while (parent >= 0 && heap[idx].compareTo(heap[parent]) < 0) {swap(idx, parent);idx = parent;parent = idx - 1 >> 1;}}/** 核心操作:下沉 */private void sink(int idx) {int left = (idx << 1) + 1; // 左子节点的索引while (left < size) { // 当存在子节点即当前节点还不是叶子节点时循环int right = left + 1;int minChild = left; // 先假设最小的子节点为左子节点if (right < size && heap[right].compareTo(heap[left]) < 0) minChild = right;if (heap[idx].compareTo(heap[minChild]) <= 0) break; // 如果已经小于等于最小子节点则完成下沉swap(idx, minChild);idx = minChild;left = (idx << 1) + 1;}}/** 将容量扩容至原来的两倍 */private void resize() {int newCapacity = heap.length * 2;T[] newHeap = (T[]) new Comparable[newCapacity];System.arraycopy(heap, 0, newHeap, 0, size);heap = newHeap;}/** 交换 heap 中两个位置的元素 */private void swap(int idx1, int idx2) {T temp = heap[idx1];heap[idx1] = heap[idx2];heap[idx2] = temp;}/** 测试 */public static void main(String[] args) {MinHeap<Integer> minHeap = new MinHeap<>();for (int i = 5; i > 0; i--) minHeap.add(i); // 按 5 4 3 2 1 的顺序插入while (!minHeap.isEmpty())System.out.print(minHeap.remove() + " "); // 1 2 3 4 5System.out.println();}
}
相关文章:
【UCB CS 61B SP24】Lecture 21: Data Structures 5: Priority Queues and Heaps 学习笔记
本文介绍了优先队列与堆,分析了最小堆的插入与删除过程,并用 Java 实现了一个通用类型的最小堆。 1. 优先队列 1.1 介绍 优先队列是一种抽象数据类型,其元素按照优先级顺序被处理。不同于普通队列的先进先出(FIFO)&…...
mapbox高阶,结合threejs(threebox)添加三维球体
👨⚕️ 主页: gis分享者 👨⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀前言1.1 ☘️mapboxgl.Map 地图对象1.2 ☘️mapboxgl.Map style属性1.3 ☘️threebox Sphere静态对象二、🍀使用t…...
QEMU源码全解析 —— 块设备虚拟化(1)
本文内容参考: 《趣谈Linux操作系统》 —— 刘超,极客时间 《QEMU/KVM源码解析与应用》 —— 李强,机械工业出版社 详解全虚拟半虚拟及硬件辅助虚拟化技术-百度开发者中心 特此致谢! 序言 本专栏之前的系列文章,讲了很多QEMU/KVM相关知识,其中一部分内容是设备的虚拟…...
IDEA中Git版本回退终极指南:Reset与Revert双方案详解
目录 前言一、版本回退前置知识二、Reset方案:整体改写历史1、IDEA图形化操作(推荐)1.1、查看提交历史1.2、选择目标版本1.3、选择回退模式1.3.1、Soft(推荐)1.3.2、Mixed1.3.3、Hard(慎用)1.3.…...
Flutter 学习之旅 之 flutter 使用 flutter_screenutil 简单进行屏幕适配
Flutter 学习之旅 之 flutter 使用 flutter_screenutil 简单进行屏幕适配 目录 Flutter 学习之旅 之 flutter 使用 flutter_screenutil 简单进行屏幕适配 一、简单介绍 二、简单介绍 flutter_screenutil 三、安装 carousel_slider 四、简单案例实现 五、关键代码 六、补…...
实验一:在Windows 10/11下配置和管理TCP/IP
目录 1.【实训目标】 2.【实训环境】 3.【实训内容】 4.【实训步骤】 1.【实训目标】 1.了解网络基本配置中包含的协议、服务、客户端。 2.了解Windows支持的网络协议及参数设置方法。 3.掌握TCP/IP协议的配置。 2.【实训环境】 硬件环境:每人一台计算机&a…...
基于hive的电信离线用户的行为分析系统
标题:基于hive的电信离线用户的行为分析系统 内容:1.摘要 随着电信行业的快速发展,用户行为数据呈现出海量、复杂的特点。为了深入了解用户行为模式,提升电信服务质量和精准营销能力,本研究旨在构建基于 Hive 的电信离线用户行为分析系统。通…...
Rust WebAssembly 入门教程
一、开发环境搭建 1. 基础工具安装 # 安装 Rust curl --proto https --tlsv1.2 -sSf https://sh.rustup.rs | sh# 安装 wasm-pack cargo install wasm-pack# 安装开发服务器 cargo install basic-http-server# 安装文件监听工具 cargo install cargo-watch2. VSCode 插件安装…...
部署RabbitMQ集群详细教程
部署RabbitMQ集群详细教程 下面是一份在 Ubuntu 环境下部署 RabbitMQ 集群的详细步骤说明,涉及主机名设置、Erlang & RabbitMQ 安装、管理插件启用、集群通信 Cookie 配置、节点加入集群、镜像队列策略设置以及集群验证等。为了演示方便,以下示例假…...
20250306JIRA添加企业微信邮箱通知
文章目录 一,参考链接如下二,补充内容1,登录企业邮箱2,设置密码3,设置收发信设置 一,参考链接如下 参考链接:https://blog.csdn.net/icett/article/details/142520823 二,补充内容…...
代码随想录算法训练营第五十七天 | 101. 孤岛的总面积 102. 沉没孤岛 103. 水流问题 104.建造最大岛屿
101. 孤岛的总面积 题目链接:KamaCoder 文档讲解:代码随想录 状态:AC Java代码: import java.util.*;class Main {static int count 0;static int res 0;static boolean island true;public static int[][] dir new int[][]{…...
llamafactory大模型微调教程(周易大模型案例)
1.环境说明 操作系统:ubuntu 20 基础模型:Qwen2.5-1.5B-Instruct 工具:llamafactory GPU:四张4090 2、环境部署 2.1 下载基础模型 # 1、下载 modelscope pip install modelscope#2、模型下载 cd /data/ cat >> download…...
excel 斜向拆分单元格
右键-合并单元格 右键-设置单元格格式-边框 在设置好分割线后,你可以开始输入文字。 需要注意的是,文字并不会自动分成上下两行。 为了达到你期望的效果,你可以通过 同过左对齐、上对齐 空格键或使用【AltEnter】组合键来调整单元格中内容的…...
【JAVA架构师成长之路】【JVM实战】第2集:生产环境内存飙高排查实战
课程标题:生产环境内存飙高排查实战——从堆转储到代码修复的15分钟指南 目标:掌握内存泄漏与OOM问题的系统性排查方法,快速定位代码或配置缺陷 0-1分钟:问题引入与核心现象 线上服务内存持续增长,触发频繁Full GC甚至OOM(OutOfMemoryError),导致服务崩溃。常见诱因:…...
MATLAB实现遗传算法优化风电_光伏_光热_储热优化
1. 问题定义 目标:最小化输出负荷与需求负荷的偏差平方和。决策变量:每个时间步长的风电、光伏、光热和储热输出功率。约束条件: 风电、光伏、光热的输出功率不得超过其最大容量。储热系统的输出功率(充放电)不得超过…...
JCRQ1河马算法+四模型对比!HO-CNN-GRU-Attention系列四模型多变量时序预测
JCRQ1河马算法四模型对比!HO-CNN-GRU-Attention系列四模型多变量时序预测 目录 JCRQ1河马算法四模型对比!HO-CNN-GRU-Attention系列四模型多变量时序预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 基于HO-CNN-GRU-Attention、CNN-GRU-Attent…...
react中的fiber和初次渲染
源码中定义了不同类型节点的枚举值 组件类型 文本节点HTML标签节点函数组件类组件等等 src/react/packages/react-reconciler/src/ReactWorkTags.js export const FunctionComponent 0; export const ClassComponent 1; export const IndeterminateComponent 2; // Befo…...
LLM 大模型基础认知篇
目录 1、基本概述 2、大模型工作原理 3、关键知识点 (1)RAG 知识库 (2)蒸馏 (3)微调 (4)智能体 1、基本概述 大型语言模型(Large Language Model, LLM)…...
leetcode700-二叉搜索树中的搜索
leetcode 700 思路 我们需要先了解一下二叉搜索树的特性: 左子树的所有节点值 < 当前节点的值。右子树的所有节点值 > 当前节点的值。这个特性适用于树中的每个节点 那么根据这个特性,我们可以通过根节点的值和目标值的大小来判断后序的走向&…...
《MySQL三大核心日志解析:Undo Log/Redo Log/Bin Log对比与实践指南》
MySQL三大核心日志解析:Undo Log/Redo Log/Bin Log对比与实践指南 一、核心日志全景概览 在MySQL数据库体系中,Undo Log、Redo Log和Bin Log构成了事务处理和数据安全的三大基石。这三大日志各司其职,协同保障了数据库的ACID特性与高可用架…...
eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...
23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...
