目录
四).为什么选择deque作为stack和queue的底层默认容器
一.stack(栈)
一).stack基本概念
1. 容器特性
-
后进先出(LIFO):最后压入的元素最先弹出
-
适配器容器:基于其他序列容器实现(默认使用deque)
-
头文件:
#include <stack> -
不支持:
-
随机访问
-
迭代器遍历
-
直接访问中间元素
-
stack<int> s1; // 默认使用deque
stack<int, vector<int>> s2; // 使用vector作为底层
stack<int, list<int>> s3; // 使用list作为底层
二).stack的函数接口
1.push()
将元素val压入stack中
int main() { std::stack<int> mystack; for (int i=0; i<5; ++i){ mystack.push(i); //0 1 2 3 4 } }
2.pop()
将stack中尾部的元素弹出
int main() { std::stack<int> mystack; for (int i = 0; i < 5; ++i) { mystack.push(i); } mystack.pop(); //4 }
3.top()
返回栈顶元素的引用
int main() { std::stack<int> mystack; for (int i = 0; i < 5; ++i) { mystack.push(i); } int a=mystack.top(); //4 }
4.size()
返回stack中元素的个数
int main() { std::stack<int> mystack; for (int i = 0; i < 5; ++i) { mystack.push(i); } int a=mystack.size(); //5 }
5.empty()
检测stack是否为空
int main() { std::stack<int> mystack; for (int i = 0; i < 5; ++i) { mystack.push(i); } bool i=mystack.empty(); //false }
stack<string> history;
// 添加浏览记录
history.push("homepage");
history.emplace("product_detail"); // 直接构造
// 回退操作
while(history.size() > 1) {
cout << "返回:" << history.top() << endl;
history.pop(); // 必须单独调用pop
}
// 安全访问
if(!history.empty()) {
history.top() = "modified_url"; // 可以修改栈顶元素
}
三). stack模拟实现
#pragma once
#include <vector>
#include <list>
#include <deque>
using namespace std;
namespace bit
{
// Container 适配转换出 stack
template<class T, class Container = deque<T>>
class stack
{
public:
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_back();
}
const T& top() const
{
return _con.back();
}
size_t size() const
{
return _con.size();
}
bool empty() const
{
return _con.empty();
}
private:
Container _con;
};
}
四). 不同底层容器的性能对比
| 操作 | deque实现 | vector实现 | list实现 |
| push() | O(1) | 平摊O(1) | O(1) |
| pop() | O(1) | O(1) | O(1) |
| top() | O(1) | O(1) | O(1) |
| 内存碎片 | 中 | 低 | 高 |
| 扩容代价 | 低 | 高 | 无 |
二.queue(队列)
一).queue基本概念
1. 容器特性
-
先进先出(FIFO):最早入队的元素最先出队
-
适配器容器:基于其他序列容器实现(默认使用
deque) -
头文件:
#include <queue> -
不支持:
-
随机访问
-
迭代器遍历
-
直接访问中间元素
-
二).queue的函数接口
大部分和stack用法一样,就不多说了
| 方法 | 说明 |
|---|---|
push(value) | 元素入队(尾部) |
emplace(args) | 直接构造元素(C++11) |
pop() | 移除队首元素(无返回值) |
front() | 访问队首元素(不删除) |
back() | 访问队尾元素(不删除) |
empty() | 判断队列是否为空 |
size() | 返回元素数量 |
queue<string> task_queue;
// 添加任务
task_queue.push("process_data");
task_queue.emplace("generate_report"); // 直接构造
// 处理任务队列
while(!task_queue.empty()) {
cout << "处理任务:" << task_queue.front() << endl;
task_queue.pop(); // 必须单独调用pop
}
// 安全访问
if(!task_queue.empty()) {
task_queue.front() = "urgent_" + task_queue.front();
}
三). queue的模拟实现
#include <deque>
using namespace std;
namespace bit
{
// Container 适配转换出 queue
template<class T, class Container = deque<T>>
class queue
{
public:
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_front();
}
const T& front() const
{
return _con.front();
}
const T& back() const
{
return _con.back();
}
size_t size() const
{
return _con.size();
}
bool empty() const
{
return _con.empty();
}
private:
Container _con;
};
}
四). 不同底层容器的性能对比
| 操作 | deque实现 | list实现 | vector实现(需支持pop_front) |
|---|---|---|---|
| push() | O(1) | O(1) | O(1) |
| pop() | O(1) | O(1) | O(n) |
| front() | O(1) | O(1) | O(1) |
| 内存碎片 | 中 | 高 | 低 |
| 适用场景 | 通用队列 | 大对象 | 不推荐 |
三.priority_queue(优先级队列 / 堆)
一).核心概念
1. 容器特性
-
优先级队列:元素按优先级顺序出队(默认最大优先)
-
适配器容器:基于其他序列容器实现(默认使用
vector) -
头文件:
#include <queue> -
底层结构:使用堆算法(默认大顶堆)
-
时间复杂度:
-
插入元素(push):O(log n)
-
删除顶部元素(pop):O(log n)
-
访问顶部元素(top):O(1)
-
priority_queue<int> pq1; // 默认vector
priority_queue<int, deque<int>> pq2; // 使用deque
priority_queue<int, vector<int>, greater<int>> pq3; // 小顶堆
支持容器必须满足随机访问迭代器(因此不能用list)
二).priority_queue 的函数接口
大部分和stack用法一样,就不多说了
| 方法 | 说明 | 时间复杂度 |
|---|---|---|
push(value) | 插入元素 | O(log n) |
emplace(args) | 构造并插入元素(C++11) | O(log n) |
pop() | 移除顶部元素 | O(log n) |
top() | 访问顶部元素(不删除) | O(1) |
empty() | 判断队列是否为空 | O(1) |
size() | 返回元素数量 | O(1) |
// 默认大顶堆
priority_queue<int> max_heap;
max_heap.push(3);
max_heap.push(1);
max_heap.push(4);
cout << max_heap.top(); // 4
// 小顶堆
priority_queue<int, vector<int>, greater<int>> min_heap;
min_heap.push(3);
min_heap.push(1);
min_heap.push(4);
cout << min_heap.top(); // 1
三).priority_queue 模拟实现
Less构建大顶堆:当父节点 < 子节点时交换(升序比较器产生降序排列)
Greater构建小顶堆:当父节点 > 子节点时交换(降序比较器产生升序排列)
#pragma once
#include <vector>
using namespace std;
// 仿函数:用于比较两个元素的'小于'关系
template<class T>
class Less {
public:
// 重载函数调用运算符,实现元素比较
bool operator()(const T& x, const T& y) {
return x < y; // 返回true表示x应该排在y前面(构建大顶堆)
}
};
// 仿函数:用于比较两个元素的'大于'关系
template<class T>
class Greater {
public:
// 重载函数调用运算符,实现元素比较
bool operator()(const T& x, const T& y) {
return x > y; // 返回true表示x应该排在y前面(构建小顶堆)
}
};
namespace bit {
template<class T, class Container = vector<T>, class Compare = Less<T>>
class priority_queue {
public:
// 向上调整(堆化)操作,维护堆结构
void AdjustUp(int child) {
Compare com; // 实例化比较器
int parent = (child - 1) / 2; // 计算父节点位置
// 循环直到到达根节点或满足堆条件
while (child > 0) {
// 关键比较:判断父节点是否需要下沉
if (com(_con[parent], _con[child])) {
// 交换父子节点值
swap(_con[parent], _con[child]);
// 更新节点位置
child = parent;
parent = (child - 1) / 2;
}
else {
break; // 堆条件已满足,退出循环
}
}
}
// 插入元素操作
void push(const T& x) {
_con.push_back(x); // 在容器尾部添加新元素
AdjustUp(_con.size() - 1); // 从最后位置开始上滤
}
// 向下调整(堆化)操作,维护堆结构
void AdjustDown(int parent) {
Compare com; // 实例化比较器
size_t child = parent * 2 + 1; // 初始左子节点位置
while (child < _con.size()) {
// 选择优先级更高的子节点(大顶堆选更大者,小顶堆选更小者)
if (child + 1 < _con.size() && com(_con[child], _con[child + 1])) {
++child; // 右子节点优先级更高
}
// 关键比较:判断父节点是否需要下沉
if (com(_con[parent], _con[child])) {
swap(_con[child], _con[parent]); // 交换父子节点
// 更新节点位置
parent = child;
child = parent * 2 + 1;
}
else {
break; // 堆条件已满足,退出循环
}
}
}
// 删除堆顶元素操作
void pop() {
// 将堆顶元素与末尾元素交换
swap(_con[0], _con[_con.size() - 1]);
_con.pop_back(); // 删除原堆顶元素
if (!empty()) {
AdjustDown(0); // 从根节点开始下滤
}
}
// 访问堆顶元素(常量引用返回)
const T& top() {
return _con[0]; // 返回容器首元素(堆顶)
}
// 获取队列元素数量
size_t size() const {
return _con.size();
}
// 判断队列是否为空
bool empty() const {
return _con.empty();
}
private:
Container _con; // 底层存储容器(默认vector)
};
}
#include<iostream>
#include<vector>
#include<list>
#include<stack>
#include<queue>
#include<algorithm>
using namespace std;
#include"priority_queue.h"
int main()
{
priority_queue<int> pq;
//bit::priority_queue<int, vector<int>, Greater<int>> pq;
pq.push(4);
pq.push(1);
pq.push(5);
pq.push(7);
pq.push(9);
while (!pq.empty())
{
cout << pq.top() << " ";
pq.pop();
}
cout << endl;
return 0;
}
这里我们使用到了仿函数,来了解一下:
四).仿函数
仿函数(Function Object):
-
一种行为类似函数的对象
-
通过重载
operator()实现函数调用语义 -
可携带状态(成员变量)的智能函数
// 小于比较仿函数
struct Less {
template <typename T>
bool operator()(const T& a, const T& b) const {
return a < b;
}
};
// 大于比较仿函数
struct Greater {
template <typename T>
bool operator()(const T& a, const T& b) const {
return a > b;
}
};
// 使用示例
int main() {
Less less;
Greater greater;
std::cout << less(3, 5) << std::endl; // 输出1 (true)
std::cout << greater(3, 5) << std::endl; // 输出0 (false)
return 0;
}
四.deque(双端队列)(了解)
一).核心概念
1. 容器特性
-
双端队列(Double-ended queue):支持两端高效插入/删除
-
头文件:
#include <deque> -
核心能力:
-
随机访问(O(1)时间复杂度)
-
头尾插入/删除(O(1)时间复杂度)
-
自动扩容(分块连续内存)
-
-
内存结构:由多个固定大小的块(buffer)组成的分段连续空间
2. 时间复杂度对比
| 操作 | deque | vector | list |
|---|---|---|---|
| 头部插入 | O(1) | O(n) | O(1) |
| 尾部插入 | O(1) | O(1)* | O(1) |
| 中间插入 | O(n) | O(n) | O(1) |
| 随机访问 | O(1) | O(1) | O(n) |
| 内存连续性 | 分段连续 | 完全连续 | 完全不连续 |
vector的尾部插入为平摊O(1)
二).deque的函数接口
| 方法 | 说明 | 示例 |
|---|---|---|
push_front(value) | 头部插入 | d.push_front(10); |
push_back(value) | 尾部插入 | d.push_back(20); |
pop_front() | 删除头部元素 | d.pop_front(); |
pop_back() | 删除尾部元素 | d.pop_back(); |
operator[] | 随机访问(不检查边界) | int x = d[5]; |
front() | 访问首元素 | d.front() = 100; |
back() | 访问末元素 | d.back() += 50; |
size() | 返回元素数量 | if(d.size() > 100) |
empty() | 判断是否为空 | while(!d.empty()) |
三).实现原理

-
中控器(map):存储指向各内存块的指针
-
数据块(buffer):固定大小的连续内存块(典型大小512字节)
-
迭代器结构:包含4个指针
-
current:当前元素位置
-
first:当前块起始位置
-
last:当前块结束位置
-
node:中控器中的位置
-
四).为什么选择deque作为stack和queue的底层默认容器
stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()和pop_back()操作的线性结构,都可以作为stack的底层容器,比如vector和list都可以;queue是先进先出的特殊线性数据结构,只要具有push_back和pop_front操作的线性结构,都可以作为queue的底层容器,比如list。但是STL中对stack和queue默认选择deque作为其底层容器,主要是因为:
1. stack和queue不需要遍历(因此stack和queue没有迭代器),只需要在固定的一端或者两端进行操作。
2. 在stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据);queue中的元素增长时,deque不仅效率高,而且内存使用率高。
结合了deque的优点,而完美的避开了其缺陷。






3864

被折叠的 条评论
为什么被折叠?



