【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特性与高可用架…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...
[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...
DAY 26 函数专题1
函数定义与参数知识点回顾:1. 函数的定义2. 变量作用域:局部变量和全局变量3. 函数的参数类型:位置参数、默认参数、不定参数4. 传递参数的手段:关键词参数5 题目1:计算圆的面积 任务: 编写一…...
聚六亚甲基单胍盐酸盐市场深度解析:现状、挑战与机遇
根据 QYResearch 发布的市场报告显示,全球市场规模预计在 2031 年达到 9848 万美元,2025 - 2031 年期间年复合增长率(CAGR)为 3.7%。在竞争格局上,市场集中度较高,2024 年全球前十强厂商占据约 74.0% 的市场…...
SQL进阶之旅 Day 22:批处理与游标优化
【SQL进阶之旅 Day 22】批处理与游标优化 文章简述(300字左右) 在数据库开发中,面对大量数据的处理任务时,单条SQL语句往往无法满足性能需求。本篇文章聚焦“批处理与游标优化”,深入探讨如何通过批量操作和游标技术提…...
6.计算机网络核心知识点精要手册
计算机网络核心知识点精要手册 1.协议基础篇 网络协议三要素 语法:数据与控制信息的结构或格式,如同语言中的语法规则语义:控制信息的具体含义和响应方式,规定通信双方"说什么"同步:事件执行的顺序与时序…...
