【Java集合夜话】第8篇上:PriorityQueue优先队列详解,从源码到实战,一文吃透堆数据结构(建议收藏)

本文深入剖析Java中的优先队列PriorityQueue,从堆的基本概念到源码实现原理,带你全面理解这个重要的数据结构。由于内容较多,分为上下两篇,本篇是上篇,主要讲解基础概念和源码分析。

系列专栏推荐:

  • JAVA集合【夜话集】
  • JVM知识专栏
  • 数据库sql理论与实战【博主踩坑之道】
  • 小游戏开发【博主强推 匠心之作 拿来即用无门槛】

【Java集合夜话】第8篇上:PriorityQueue优先队列详解,从源码到实战,一文吃透堆数据结构(建议收藏)_第1张图片

文章目录

    • 1. 优先队列基础
      • 1.1 什么是优先队列?
        • 1.1.1 与普通队列的区别
        • 1.1.2 优先级的概念
        • 1.1.3 实际应用场景
      • 1.2 底层数据结构:堆
        • 1.2.1 什么是堆?
        • 1.2.2 最大堆vs最小堆
        • 1.2.3 完全二叉树的性质
        • 1.2.4 堆的基本操作
        • 1.2.5 堆排序:一个有趣的应用
    • 2. 核心原理
      • 2.1 数组实现堆的精妙之处
        • 2.1.1 父子节点的完美映射
        • 2.1.2 上浮(siftUp)操作的实现
        • 2.1.3 下沉(siftDown)操作的实现
        • 2.1.4 堆化(heapify)的魔法
      • 2.2 关键操作的实现
        • 2.2.1 插入元素(offer)
        • 2.2.2 获取并删除堆顶元素(poll)
        • 2.2.3 查看堆顶元素(peek)
        • 2.2.4 删除指定元素(remove)
    • 3. 源码分析
      • 3.1 核心属性和构造方法
        • 3.1.1 核心属性
        • 3.1.2 构造方法
        • 3.1.3 特殊构造方法
      • 3.2 核心方法实现
        • 3.2.1 add/offer方法
        • 3.2.2 扩容机制
        • 3.2.3 删除操作
        • 3.2.4 查找操作
        • 3.2.5 迭代器实现
    • 写在最后

1. 优先队列基础

1.1 什么是优先队列?

想象你正在医院的急诊室。虽然病人按到达顺序排队,但突然来了一位急重症病人,医生会优先处理这位病人。这就是现实生活中的"优先队列":不是先来先服务,而是按照优先级处理

1.1.1 与普通队列的区别

普通队列就像我们排队买奶茶:

  • 先来的人先买
  • 后来的人后买
  • 严格遵循"先进先出"(FIFO)原则

而优先队列则像医院的急诊室:

  • 不是先来先服务
  • 而是按照事件的紧急程度
  • 优先级高的先处理
  • 优先级相同时,先来先服务

让我们用代码来直观感受一下区别:

// 普通队列
Queue<String> normalQueue = new LinkedList<>();
normalQueue.offer("小明");
normalQueue.offer("小红");
normalQueue.offer("小张");
// 处理顺序:小明 -> 小红 -> 小张

// 优先队列
PriorityQueue<Patient> emergencyRoom = new PriorityQueue<>((p1, p2) -> 
    p2.getEmergencyLevel() - p1.getEmergencyLevel());
emergencyRoom.offer(new Patient("小明", 1)); // 普通感冒
emergencyRoom.offer(new Patient("小红", 3)); // 高烧
emergencyRoom.offer(new Patient("小张", 2)); // 扭伤
// 处理顺序:小红(高烧) -> 小张(扭伤) -> 小明(感冒)
1.1.2 优先级的概念

优先级可以基于多种规则:

  1. 自然顺序
  • 数字:越小越优先
  • 字符串:字典序越小越优先
// 数字优先队列(小顶堆)
PriorityQueue<Integer> numbers = new PriorityQueue<>();
numbers.offer(3);
numbers.offer(1);
numbers.offer(2);
System.out.println(numbers.poll()); // 输出1
  1. 自定义顺序
  • 根据业务需求定义优先级
  • 通过Comparator实现自定义排序
// 工作任务队列(按优先级排序)
PriorityQueue<Task> taskQueue = new PriorityQueue<>((t1, t2) -> {
   
    // 先比较优先级
    if (t1.getPriority() != t2.getPriority()) {
   
        return t2.getPriority() - t1.getPriority();
    }
    // 优先级相同,比较创建时间
    return t1.getCreateTime().compareTo(t2.getCreateTime());
});
1.1.3 实际应用场景

优先队列在我们的日常生活和系统开发中随处可见:

  1. 操作系统的进程调度
  • 高优先级进程优先获得CPU时间片
  • 系统进程优先级高于用户进程
  • 前台进程优先级高于后台进程
  1. 打印机任务队列
  • 紧急文件优先打印
  • 老板的文件优先级更高
  • 大文件可能优先级较低
  1. 游戏排位匹配系统
class Player {
   
    private String name;
    private int ranking;
    private long waitTime;
    // ... 其他属性
}

PriorityQueue<Player> matchQueue = new PriorityQueue<>((p1, p2) -> {
   
    // 等待时间越长,优先级越高
    long timeDiff = p2.getWaitTime() - p1.getWaitTime();
    if (timeDiff != 0) {
   
        return (int)timeDiff;
    }
    // 等待时间相同,按照排位分数匹配
    return Math.abs(p1.getRanking() - targetRanking) - 
           Math.abs(p2.getRanking() - targetRanking);
});
  1. 网络请求处理
  • VIP用户请求优先处理
  • 支付请求优先于查询请求
  • 实时性要求高的请求优先

通过这些例子,我们可以看到优先队列的核心特点:

  • 动态调整处理顺序
  • 基于优先级的灵活排序
  • 在插入时自动维护顺序
  • 保证每次取出的都是当前最优先的元素

理解了优先队列的本质,我们就能更好地在实际开发中运用它,设计出更合理的系统。接下来,我们将深入探讨它的底层实现原理——堆数据结构。

1.2 底层数据结构:堆

要理解优先队列,我们必须先理解它的底层数据结构——堆。想象一个公司的组织架构:CEO在最顶端,下面是经理,再下面是普通员工。堆的结构也是类似的层级关系。

1.2.1 什么是堆?

堆是一种特殊的完全二叉树,它满足以下特性:

  • 结构性:是一个完全二叉树
  • 有序性:任意节点的值总是大于(或小于)其子节点的值

就像公司架构:

  • 完全二叉树:每层都尽可能填满,新人从左到右依次加入
  • 有序性:职级高的总是在上层(类比大顶堆)
1.2.2 最大堆vs最小堆
  1. 最大堆(大顶堆)
  • 父节点的值总是大于或等于子节点

你可能感兴趣的:(java基础,Java面试,Java集合,java,数据结构,PriorityQueue,优先队列,堆数据结构)