1. 优先队列(priority_queue)的核心概念与特性
优先队列是一种特殊的队列数据结构,它遵循"最大元素优先"的原则。与普通队列的先进先出(FIFO)不同,优先队列中的元素按照优先级顺序出队。这种特性使得优先队列在任务调度、图算法(如Dijkstra最短路径算法)等场景中非常有用。
1.1 堆与优先队列的关系
优先队列通常使用堆(Heap)作为其底层实现。堆是一种完全二叉树,满足以下性质:
- 大顶堆:每个节点的值都大于或等于其子节点的值
- 小顶堆:每个节点的值都小于或等于其子节点的值
在C++ STL中,priority_queue默认使用vector作为底层容器,并通过堆算法维护元素的优先级顺序。这种设计选择基于以下考虑:
- vector支持随机访问,这对堆操作至关重要
- vector的内存布局紧凑,缓存友好
- vector的动态扩容机制适合大多数使用场景
注意:虽然deque也可以作为底层容器,但由于其内存布局不如vector连续,在性能敏感的场景下,vector通常是更好的选择。
1.2 优先队列的基本操作复杂度
| 操作 | 时间复杂度 | 说明 |
|---|---|---|
| push() | O(log n) | 插入元素并维护堆性质 |
| pop() | O(log n) | 移除堆顶元素并维护堆性质 |
| top() | O(1) | 访问堆顶元素 |
| empty() | O(1) | 检查队列是否为空 |
| size() | O(1) | 获取队列元素数量 |
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. priority_queue的深度使用技巧
2.1 自定义比较函数
priority_queue的灵活性很大程度上来自于其可定制的比较函数。除了使用默认的less(大顶堆)和greater(小顶堆)外,我们可以定义自己的比较逻辑:
cpp复制struct CustomCompare {
bool operator()(const pair<int, string>& a, const pair<int, string>& b) {
// 先按整数值降序,再按字符串升序
if (a.first != b.first)
return a.first < b.first;
return a.second > b.second;
}
};
void customComparisonDemo() {
priority_queue<pair<int, string>,
vector<pair<int, string>>,
CustomCompare> pq;
pq.push({3, "apple"});
pq.push({1, "banana"});
pq.push({3, "cherry"});
pq.push({2, "date"});
while (!pq.empty()) {
auto item = pq.top();
cout << item.first << ": " << item.second << endl;
pq.pop();
}
}
2.2 高效初始化技巧
当我们需要用已有数据初始化优先队列时,直接使用迭代器构造比逐个push更高效:
cpp复制vector<int> data = {5, 2, 8, 3, 1, 6};
// 低效方式 - O(n log n)
priority_queue<int> pq1;
for (int num : data) {
pq1.push(num); // 每次push都是O(log n)
}
// 高效方式 - O(n)
priority_queue<int> pq2(data.begin(), data.end());
背后的原理是:直接使用迭代器构造时,STL会先构建完全二叉树,然后从最后一个非叶子节点开始向下调整(heapify),这种批量建堆的时间复杂度是O(n),而逐个插入是O(n log n)。
3. 优先队列的底层实现剖析
3.1 核心算法实现
优先队列的核心在于两个关键算法:向上调整(AdjustUp)和向下调整(AdjustDown)。让我们深入分析它们的实现细节:
cpp复制// 向下调整算法(维护堆性质的关键)
void AdjustDown(Container& a, int n, int root) {
int parent = root;
int child = parent * 2 + 1; //
