知用网
柔彩主题三 · 更轻盈的阅读体验

数据结构优先队列:让任务按重要性自动排序

发布时间:2026-01-12 18:30:52 阅读:85 次

什么是优先队列

在写程序时,我们经常需要处理一堆任务。比如医院急诊室要安排病人就诊,不能谁来得早就先看谁,得看谁的病情更严重。这时候普通的“先来后到”队列就不够用了,得用一种更聪明的结构——优先队列。

优先队列本质上是一种特殊的队列,它不按插入顺序出队,而是按元素的“优先级”来决定谁先出。优先级最高的元素总是最先被取出,哪怕它是最后一个进来的。

和普通队列的区别

普通队列就像排队买奶茶,先到的人先买到。代码里通常是 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),虽然不错,但如果你只是偶尔查个最大值,直接遍历数组反而更省事。

另外,如果多个任务优先级一样,大多数实现不会保证它们之间的顺序稳定。也就是说,两个同优先级的任务,可能后进的先出,这点在某些场景下要注意。

还有,别忘了定义清楚“优先”的标准。是数字大优先?还是时间早优先?有时候业务逻辑一变,优先规则就得跟着改。