Bootstrap

Priority Queue优先队列详解

1. 优先队列的定义

优先队列(Priority Queue)是一种抽象数据类型,它类似于普通队列,遵循先进先出(FIFO)原则,但在此基础上,每个元素都关联着一个优先级。元素按照优先级顺序被处理,优先级高的元素会优先于优先级低的元素出队,而并非严格按照元素入队的先后顺序。简单来说,它是一种能保证每次取出的元素都是当前队列中优先级最高的元素的数据结构。

2. 适用范围和注意事项

  • 适用范围
    • 任务调度场景:在多任务处理系统中,不同任务往往具有不同的紧急程度或重要性(即优先级),例如在操作系统中,实时性要求高的任务(如处理硬件中断)优先级高于普通的后台任务(如文件系统的定期清理),可以利用优先队列来安排任务执行顺序,确保高优先级任务先被执行。
    • 数据排序应用:可作为一些排序算法的基础,比如堆排序。通过将待排序的数据依次插入优先队列(根据是升序还是降序需求选择最小堆或最大堆),然后依次取出元素,就能得到有序的数据序列,实现排序功能。
    • 事件驱动模拟:在模拟现实世界中的事件顺序时,比如交通流量模拟中车辆到达路口的先后顺序、网络数据包传输中不同数据包的处理顺序等场景,每个事件都有其发生时间或重要程度作为优先级,优先队列可用于管理这些事件,决定下一个要处理的事件。
  • 注意事项
    • 优先级定义的一致性:在使用优先队列时,需要明确且合理地定义元素的优先级规则,并且整个使用过程中要保持该规则的一致性。例如,如果按照数值大小定义优先级(数值小的优先级高或者数值大的优先级高),那么所有插入和比较操作都要遵循这一规则,否则可能导致元素取出顺序不符合预期。
    • 性能考量:虽然优先队列能高效地处理基于优先级的元素操作,但不同的实现方式(如基于数组的二叉堆实现、基于链表实现等)在不同场景下的性能表现有差异。在处理大规模数据时,要根据实际情况选择合适的实现方式,例如基于二叉堆实现的优先队列在插入和删除操作时时间复杂度为 ,但如果不合理使用,频繁地调整优先级等操作可能会累积一定的性能开销。
    • 元素对象的可比较性:在 Java 等编程语言中,放入优先队列的元素通常需要具备可比较性,要么元素本身实现了 Comparable 接口来定义自然的比较规则(如 IntegerString 等基本数据类型的包装类都实现了该接口),要么在创建优先队列时提供一个自定义的 Comparator 实现类来明确比较规则,否则会出现运行时错误,无法正确判断元素的优先级顺序。

3. 主要的 API(以 Java 为例)

在 Java 中,java.util.PriorityQueue 类实现了优先队列的数据结构,常用的 API 如下:

  • 构造函数
    • PriorityQueue():创建一个默认初始容量为 11 的优先队列,元素按照自然顺序排序(要求元素实现 Comparable 接口)。
    •        PriorityQueue<Integer> pq1 = new PriorityQueue<>();
             pq1.add(3);
             pq1.add(1);
             pq1.add(2);
             // 此时pq1中的元素顺序为1, 3, 2(最小堆,每次取出最小元素)
    • PriorityQueue(int initialCapacity):创建一个指定初始容量的优先队列,同样按照自然顺序排序元素。
    •        PriorityQueue<String> pq2 = new PriorityQueue<>(5);
             pq2.add("apple");
             pq2.add("banana");
             pq2.add("cherry");
             // 假设按照字典序排序,具体顺序取决于Comparable接口实现
    • PriorityQueue(Comparator<? super E> comparator):创建一个优先队列,并使用传入的 Comparator 来定义元素的优先级比较规则,初始容量默认是 11。
    •        PriorityQueue<Integer> pq3 = new PriorityQueue<>(Comparator.reverseOrder());
             pq3.add(3);
             pq3.add(1);
             pq3.add(2);
             // 此时pq3中的元素顺序为3, 2, 1(最大堆,每次取出最大元素)
    • PriorityQueue(int initialCapacity, Comparator<? super E> comparator):创建一个既有指定初始容量,又通过传入的 Comparator 定义优先级比较规则的优先队列。
             PriorityQueue<Student> pq4 = new PriorityQueue<>(3, (s1, s2) -> s2.getScore() - s1.getScore());
             // 假设Student类有getScore方法获取成绩,这里定义成绩高的学生优先级高
  • 插入元素操作
    • boolean add(E e):将指定元素添加到优先队列中,如果插入成功返回 true,如果队列已满(受限于容量和内存等因素)且无法插入则抛出异常(通常是 IllegalStateException)。此方法是 Collection 接口定义的方法,在 PriorityQueue 中用于添加元素。
    • boolean offer(E e):和 add 方法类似,将指定元素添加到优先队列中,但如果插入失败(如队列已满等情况)不会抛出异常,而是返回 false,相对来说更 “友好” 一些,常用于在不确定插入是否一定能成功的场景下添加元素。
  • 获取和删除堆顶元素操作
    • E peek():获取但不删除优先队列的队首元素(即优先级最高的元素),如果队列为空则返回 null
    • E poll():获取并删除优先队列的队首元素,如果队列为空则返回 null。此操作会调整队列结构,使下一个优先级最高的元素成为队首元素。
  • 其他常用API
    • int size()
    • 功能:返回优先队列中元素的个数。
    • void clear()
    • 功能:清空优先队列中的所有元素。
    • boolean isEmpty()
    • 功能:判断优先队列是否为空。如果队列为空返回true,否则返回false

4. 使用 Java 举例一个优先队列的例子

以下是一个简单的 Java 示例代码,展示如何使用优先队列来管理学生成绩信息,按照成绩从高到低的顺序输出学生姓名(这里假设成绩越高优先级越高):

import java.util.Comparator;
import java.util.PriorityQueue;

class Student {
    private String name;
    private int score;

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }

    public String getName() {
        return name;
    }

    public int getScore() {
        return score;
    }
}

public class PriorityQueueExample {
    public static void main(String[] args) {
        // 使用自定义的Comparator来定义按照成绩从高到低排序的优先级规则
        Comparator<Student> scoreComparator = (s1, s2) -> s2.getScore() - s1.getScore();

        PriorityQueue<Student> studentQueue = new PriorityQueue<>(scoreComparator);

        // 添加学生信息到优先队列
        studentQueue.add(new Student("Alice", 90));
        studentQueue.add(new Student("Bob", 85));
        studentQueue.add(new Student("Charlie", 95));

        // 依次取出并输出学生信息,按照成绩从高到低的顺序
        while (!studentQueue.isEmpty()) {
            Student student = studentQueue.poll();
            System.out.println("Name: " + student.getName() + ", Score: " + student.getScore());
        }
    }
}

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;