什么是优先队列
在写程序时,我们经常需要处理一堆任务。比如医院急诊室要安排病人就诊,不能谁来得早就先看谁,得看谁的病情更严重。这时候普通的“先来后到”队列就不够用了,得用一种更聪明的结构——优先队列。
优先队列本质上是一种特殊的队列,它不按插入顺序出队,而是按元素的“优先级”来决定谁先出。优先级最高的元素总是最先被取出,哪怕它是最后一个进来的。
和普通队列的区别
普通队列就像排队买奶茶,先到的人先买到。代码里通常是 FIFO(先进先出):
queue.enqueue("小明");
queue.enqueue("小红");
queue.dequeue(); // 小明先出
而优先队列更像是航班登机:头等舱、紧急乘客可能比经济舱早登机,哪怕他们来得晚。每个人进去的时候都带了个“优先级标签”。
怎么实现一个优先队列
最常用的实现方式是用堆(Heap),尤其是二叉堆。最大堆能让最高优先级的元素始终在堆顶,每次取最大值的时间效率很高。
下面是一个用 Python 实现的简单例子,使用内置的 heapq 模块:
import heapq
class PriorityQueue:
def __init__(self):
self._queue = []
self._index = 0
def push(self, item, priority):
# 传入负优先级实现最大堆效果
heapq.heappush(self._queue, (-priority, self._index, item))
self._index += 1
def pop(self):
return heapq.heappop(self._queue)[-1]
# 使用示例
pq = PriorityQueue()
pq.push("普通报告", 1)
pq.push("紧急bug修复", 5)
pq.push("系统重启", 3)
print(pq.pop()) # 输出:紧急bug修复
print(pq.pop()) # 输出:系统重启
print(pq.pop()) # 输出:普通报告
这里用了一个小技巧:把优先级取负值,因为 heapq 默认是最小堆。还加了个 _index 避免相同优先级时比较对象出错。
实际应用场景
优先队列不是纸上谈兵,很多真实系统都在用。
比如操作系统调度进程:CPU 不会傻傻按顺序执行每个任务,而是优先处理高响应需求的程序,比如正在打游戏或视频通话的应用。
再比如外卖平台派单,骑手离得近是一方面,但更重要的是订单快超时了。系统会把即将超时的订单优先级调高,推给最近的骑手,这就是典型的优先队列逻辑。
常见语言中的支持
Java 有 PriorityQueue 类:
PriorityQueue<String> pq = new PriorityQueue<>((a, b) -> b.length() - a.length());
pq.add("hi");
pq.add("hello world");
System.out.println(pq.poll()); // hello world 先出
C++ 的 std::priority_queue 也类似:
#include <queue>
#include <string>
struct Task {
std::string name;
int priority;
};
auto cmp = [](const Task& a, const Task& b) { return a.priority < b.priority; };
std::priority_queue<Task, std::vector<Task>, decltype(cmp)> pq(cmp);
pq.push({"清理日志", 2});
pq.push({"数据库备份", 4});
std::cout << pq.top().name; // 输出:数据库备份
注意点和坑
别以为优先队列啥都能加速。插入和删除的时间复杂度一般是 O(log n),虽然不错,但如果你只是偶尔查个最大值,直接遍历数组反而更省事。
另外,如果多个任务优先级一样,大多数实现不会保证它们之间的顺序稳定。也就是说,两个同优先级的任务,可能后进的先出,这点在某些场景下要注意。
还有,别忘了定义清楚“优先”的标准。是数字大优先?还是时间早优先?有时候业务逻辑一变,优先规则就得跟着改。