C++ 中 priority_queue 和 deque 的使用与实现
在 C++ 标准模板库(STL)中,priority_queue 和 deque 是两个重要的容器,分别用于高效管理优先级元素和双端操作。下面我将逐步解释它们的使用方法、实现原理,并提供代码示例。内容基于 C++ 标准库(如 C++11 及以上),确保真实可靠。
一、priority_queue 的使用与实现
priority_queue 是一个优先队列容器,元素按优先级排序(默认最大堆,即顶部元素最大)。它适用于需要快速访问最高或最低优先级元素的场景,如任务调度或堆排序。
1. 使用方法
- 包含头文件:#include <queue>
- 声明:priority_queue<T, Container, Compare> pq;
- T:元素类型,如 int。
- Container:底层容器,默认为 vector<T>。
- Compare:比较函数,默认为 less<T>(最大堆),可使用 greater<T> 改为最小堆。
- 常用操作:
- push(val):插入元素。
- pop():删除顶部元素(最高优先级)。
- top():访问顶部元素。
- empty():检查队列是否为空。
- size():获取元素数量。
示例代码:
#include <iostream>
#include <queue>
using namespace std;
int main() {
// 默认最大堆
priority_queue<int> pq;
pq.push(30);
pq.push(10);
pq.push(20);
cout << "Top element: " << pq.top() << endl; // 输出 30
pq.pop(); // 删除顶部元素
cout << "New top: " << pq.top() << endl; // 输出 20
// 最小堆示例
priority_queue<int, vector<int>, greater<int>> min_pq;
min_pq.push(5);
min_pq.push(3);
min_pq.push(4);
cout << "Min top: " << min_pq.top() << endl; // 输出 3
return 0;
}
2. 实现原理 priority_queue 底层通常基于堆数据结构实现。堆是一个完全二叉树,满足堆属性:父节点优先级高于子节点(最大堆)或低于子节点(最小堆)。操作时间复杂度:
- 插入(push):$O(\\log n)$,通过上浮(heapify up)调整。
- 删除顶部(pop):$O(\\log n)$,通过下沉(heapify down)调整。
- 访问顶部(top):$O(1)$。
在 C++ 中,priority_queue 使用底层容器(如 vector)存储元素,并通过 make_heap、push_heap 和 pop_heap 算法维护堆结构。例如,索引 $i$ 的父节点索引为 $\\lfloor (i-1)/2 \\rfloor$,子节点为 $2i+1$ 和 $2i+2$。
二、deque 的使用与实现
deque(双端队列)支持在两端高效插入和删除元素,适用于需要频繁操作队列头尾的场景,如滑动窗口或缓冲区管理。
1. 使用方法
- 包含头文件:#include <deque>
- 声明:deque<T> dq;
- 常用操作:
- push_back(val):在尾部插入元素。
- push_front(val):在头部插入元素。
- pop_back():删除尾部元素。
- pop_front():删除头部元素。
- front():访问头部元素。
- back():访问尾部元素。
- empty()、size():类似队列操作。
- 支持随机访问:dq[index] 或 dq.at(index)。
示例代码:
#include <iostream>
#include <deque>
using namespace std;
int main() {
deque<int> dq;
dq.push_back(1); // 尾部插入
dq.push_front(2); // 头部插入
dq.push_back(3);
cout << "Front: " << dq.front() << ", Back: " << dq.back() << endl; // 输出 Front: 2, Back: 3
dq.pop_front(); // 删除头部
cout << "New front: " << dq.front() << endl; // 输出 1
return 0;
}
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTY1NS5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTU3Mi5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTU2OS5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTQ5NC5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTcyNC5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTU2Ni5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTcyMC5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTQ4Ni5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTQzMy5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTQyNi5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTI3Ni5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTI4My5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTI3Mi5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTI2NC5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTQxNi5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTI4NC5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTUyNi5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTQ0NC5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTc0MC5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTgxNC5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTU5Mi5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTY3NC5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTgyMS5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTgyNC5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTkxMS5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTc2MC5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTgzOC5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTgzOS5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTkyMC5zaHRtbA==.html
https://tv.sohu.com/v/dXMvNDQyMzEwMjg0LzcwMjIxNTg0MS5zaHRtbA==.html
2. 实现原理 deque 通常基于分块数组(或称为“块链表”)实现。它将内存分为多个固定大小的块(chunk),每个块存储部分元素。这种结构允许:
- 高效的两端操作:在头部或尾部插入/删除的时间复杂度为 $O(1)$(分摊)。
- 随机访问:通过索引计算块位置和偏移,时间复杂度 $O(1)$。
具体实现中,deque 维护一个指针数组(map),指向各个内存块。例如,访问索引 $i$ 的元素时,计算块索引为 $\\lfloor i / \\text{chunk_size} \\rfloor$,块内偏移为 $i \\mod \\text{chunk_size}$。这种设计避免了 vector 在头部插入时的拷贝开销。
三、比较与总结
- priority_queue vs. deque:
- priority_queue 适合优先级管理,但只支持顶部访问;deque 支持两端操作和随机访问。
- 时间复杂度:
- priority_queue 插入/删除:$O(\\log n)$,访问顶部:$O(1)$。
- deque 两端操作:$O(1)$,随机访问:$O(1)$。
- 底层容器:priority_queue 默认用 vector,deque 用分块数组。
在实际应用中,选择取决于需求:优先队列用于排序相关任务,双端队列用于序列管理。结合代码示例,您可以在 C++ 项目中灵活使用这些容器。如果有更多细节问题,欢迎进一步讨论!





