一、优先队列基础概念
优先队列(Priority Queue) 是一种特殊的队列数据结构,其中每个元素都有优先级,元素按照优先级顺序出队(而非插入顺序)。核心特性:
- 高优先级元素先出队
- 相同优先级按特定规则处理(如插入顺序)
- 支持动态插入和删除
1.1 与普通队列对比
特性 | 普通队列 | 优先队列 |
---|---|---|
出队顺序 | 先进先出(FIFO) | 按优先级排序 |
时间复杂度 | O(1)出/入队 | O(log n)出/入队 |
应用场景 | 顺序处理任务 | 紧急任务优先处理 |
底层实现 | 链表/数组 | 堆结构 |
1.2 核心操作
- 插入(enqueue):添加元素到队列
- 删除(dequeue):移除最高优先级元素
- 查看(peek):查看最高优先级元素
- 大小(size):返回元素数量
- 判空(isEmpty):检查队列是否为空
二、堆:优先队列的基石
堆是一种特殊的完全二叉树,是优先队列的高效实现方式。
2.1 堆的类型
最小堆:父节点值 ≤ 子节点值(根节点最小)
最大堆:父节点值 ≥ 子节点值(根节点最大)
2.2 堆的操作
插入(上浮):
void insert(int[] heap, int val) {
heap[size] = val; // 放入末尾
int i = size++;
while (i > 0 && heap[i] < heap[(i-1)/2]) { // 最小堆上浮
swap(heap, i, (i-1)/2);
i = (i-1)/2;
}
}
删除(下沉):
int extractMin(int[] heap) {
int min = heap[0];
heap[0] = heap[--size]; // 末尾元素移到根
int i = 0;
while (2*i+1 < size) { // 有子节点
int child = 2*i+1;
if (child+1 < size && heap[child+1] < heap[child])
child++; // 选择较小子节点
if (heap[i] <= heap[child]) break;
swap(heap, i, child);
i = child;
}
return min;
}
2.3 堆的构建
三、Java中的PriorityQueue
Java提供了内置的优先队列实现java.util.PriorityQueue
。
3.1 核心特性
- 基于最小堆实现(默认)
- 线程不安全(多线程用
PriorityBlockingQueue
) - 不允许null元素
- 动态扩容(默认初始容量11)
3.2 构造方法
// 默认最小堆
PriorityQueue<Integer> minHeap = new PriorityQueue<>();
// 指定初始容量
PriorityQueue<Integer> heap = new PriorityQueue<>(100);
// 创建最大堆
PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a);
// 从集合初始化
PriorityQueue<Integer> heapFromColl = new PriorityQueue<>(List.of(3,1,4));
3.3 常用方法
// 插入元素
heap.offer(5); // 推荐(返回boolean)
heap.add(5); // 可能抛出异常
// 移除并返回队首
int min = heap.poll();
// 查看队首
int min = heap.peek();
// 其他操作
heap.size(); // 元素数量
heap.clear(); // 清空队列
heap.contains(5); // 检查包含
3.4 自定义对象排序
class Task implements Comparable<Task> {
int priority;
String name;
public int compareTo(Task other) {
return this.priority - other.priority; // 按优先级排序
}
}
// 使用
PriorityQueue<Task> taskQueue = new PriorityQueue<>();
四、优先队列的应用场景
4.1 任务调度系统
class TaskScheduler {
private PriorityQueue<Task> queue = new PriorityQueue<>();
public void addTask(Task task) {
queue.offer(task);
}
public Task getNextTask() {
return queue.poll();
}
static class Task implements Comparable<Task> {
int priority; // 1=最高, 5=最低
String name;
public int compareTo(Task other) {
return this.priority - other.priority;
}
}
}
4.2 合并K个有序链表
public ListNode mergeKLists(ListNode[] lists) {
PriorityQueue<ListNode> heap = new PriorityQueue<>((a, b) -> a.val - b.val);
// 所有链表头节点入堆
for (ListNode node : lists) {
if (node != null) heap.offer(node);
}
ListNode dummy = new ListNode(0);
ListNode current = dummy;
while (!heap.isEmpty()) {
ListNode min = heap.poll();
current.next = min;
current = current.next;
if (min.next != null) {
heap.offer(min.next);
}
}
return dummy.next;
}
4.3 实时数据流的中位数
class MedianFinder {
// 最大堆存储较小一半数字
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());
// 最小堆存储较大一半数字
PriorityQueue<Integer> minHeap = new PriorityQueue<>();
public void addNum(int num) {
maxHeap.offer(num);
minHeap.offer(maxHeap.poll());
// 平衡堆大小
if (maxHeap.size() < minHeap.size()) {
maxHeap.offer(minHeap.poll());
}
}
public double findMedian() {
if (maxHeap.size() == minHeap.size()) {
return (maxHeap.peek() + minHeap.peek()) / 2.0;
} else {
return maxHeap.peek();
}
}
}
4.4 最短路径算法(Dijkstra)
void dijkstra(Graph graph, int source) {
int[] dist = new int[graph.V];
Arrays.fill(dist, Integer.MAX_VALUE);
dist[source] = 0;
// 优先队列:[距离, 节点]
PriorityQueue<int[]> heap = new PriorityQueue<>((a, b) -> a[0] - b[0]);
heap.offer(new int[]{0, source});
while (!heap.isEmpty()) {
int[] node = heap.poll();
int u = node[1];
for (Edge edge : graph.adj[u]) {
int v = edge.dest;
int weight = edge.weight;
if (dist[u] + weight < dist[v]) {
dist[v] = dist[u] + weight;
heap.offer(new int[]{dist[v], v});
}
}
}
}
4.5 游戏中的AI决策
class GameAI {
PriorityQueue<Action> actionQueue = new PriorityQueue<>();
void decideActions(Player player) {
// 评估所有可能行动
for (Action action : possibleActions) {
action.priority = calculatePriority(player, action);
actionQueue.offer(action);
}
// 执行最高优先级行动
Action bestAction = actionQueue.poll();
executeAction(bestAction);
}
int calculatePriority(Player player, Action action) {
// 根据游戏状态计算优先级
if (action.type == ATTACK && player.health < 20)
return 100; // 高优先级
if (action.type == HEAL && player.health < 30)
return 80;
return 30; // 默认优先级
}
}
五、性能优化与最佳实践
5.1 时间复杂度分析
操作 | 时间复杂度 | 说明 |
---|---|---|
插入(offer) | O(log n) | 堆的上浮操作 |
删除(poll) | O(log n) | 堆的下沉操作 |
查看(peek) | O(1) | 直接访问堆顶 |
构建堆 | O(n) | 弗洛伊德算法 |
查找元素 | O(n) | 需要遍历整个堆 |
5.2 使用场景选择指南
5.3 最佳实践
初始容量设置:
// 预估元素数量
PriorityQueue<Task> queue = new PriorityQueue<>(estimatedSize);
避免频繁扩容:
// 添加前预估容量
if (queue.size() == queue.capacity()) {
queue = new PriorityQueue<>(queue.size() * 2, queue.comparator());
}
自定义对象优化:
// 使用轻量级比较器
PriorityQueue<Point> points = new PriorityQueue<>(
Comparator.comparingInt(p -> p.x * p.x + p.y * p.y)
);
批量操作优化:
// 批量添加元素
queue.addAll(elements);
// 而不是循环添加
5.4 常见陷阱
优先级反转问题:
// 错误:在循环中修改元素优先级
for (Task t : queue) {
t.priority = calculateNewPriority(); // 不会触发重新排序
}
// 正确做法:移除->修改->重新插入
并发访问问题:
// 多线程环境下
PriorityQueue<Integer> unsafeQueue = new PriorityQueue<>();
// 使用线程安全版本
BlockingQueue<Integer> safeQueue = new PriorityBlockingQueue<>();
对象相等性判断:
// 自定义对象需正确实现equals和hashCode
class Task {
int id;
int priority;
public boolean equals(Object o) {
return this.id == ((Task)o).id;
}
}
六、高级应用:可更新优先队列
标准优先队列不支持更新元素优先级,需要自定义实现。
6.1 基于索引堆的实现
class UpdatablePriorityQueue<T> {
private final List<T> heap = new ArrayList<>();
private final Map<T, Integer> indexMap = new HashMap<>();
private final Comparator<? super T> comparator;
public void add(T item) {
heap.add(item);
indexMap.put(item, heap.size()-1);
swim(heap.size()-1);
}
public boolean update(T oldItem, T newItem) {
Integer index = indexMap.get(oldItem);
if (index == null) return false;
heap.set(index, newItem);
indexMap.remove(oldItem);
indexMap.put(newItem, index);
swim(index);
sink(index);
return true;
}
private void swim(int k) {
while (k > 0) {
int parent = (k-1)/2;
if (comparator.compare(heap.get(k), heap.get(parent)) >= 0) break;
swap(k, parent);
k = parent;
}
}
private void sink(int k) {
int size = heap.size();
while (2*k+1 < size) {
int j = 2*k+1;
if (j+1 < size && comparator.compare(heap.get(j+1), heap.get(j)) < 0) j++;
if (comparator.compare(heap.get(k), heap.get(j)) <= 0) break;
swap(k, j);
k = j;
}
}
}
6.2 应用场景:网络路由更新
class NetworkRouter {
private UpdatablePriorityQueue<Route> routes = new UpdatablePriorityQueue<>(
Comparator.comparingInt(r -> r.latency)
);
void updateRoute(Route oldRoute, Route newRoute) {
routes.update(oldRoute, newRoute);
}
Route getBestRoute() {
return routes.peek();
}
}
七、总结
- 核心价值:高效处理优先级任务
- 实现基础:堆数据结构(最小堆/最大堆)
- Java实现:
PriorityQueue
类(默认最小堆) - 应用场景:
- 任务调度系统
- 算法优化(Dijkstra,堆排序)
- 实时数据处理(中位数计算)
- 游戏AI决策
- 网络路由管理