C++容器适配器:stack与queue详解

1. 什么是容器适配器?

C++ 标准库提供了三种容器适配器:

  • stack —— 后进先出(LIFO,Last In First Out)

  • queue —— 先进先出(FIFO,First In First Out)

  • priority_queue —— 按优先级出队(本课最后会简单提及)

它们内部可以选用不同的底层容器(默认是 deque,但你也可以指定 vector 或 list),然后只暴露几个特定的接口,屏蔽了无关操作。

可以这样理解:stack 就像一摞盘子,只能动最上面那个;queue 就像排队,先来的先服务,从一端进另一端出。


2. 头文件和基本声明

#include <stack>   // 使用 stack
#include <queue>   // 使用 queue 和 priority_queue

声明一个栈和一个队列:

std::stack<int> st;          // 存放 int 的栈,底层默认用 deque<int>
std::queue<std::string> q;   // 存放 string 的队列,底层默认用 deque<std::string>

如果需要指定底层容器(通常没必要,除非有特殊性能需求):

std::stack<int, std::vector<int>> st_vec;   // 栈,底层用 vector
std::queue<int, std::list<int>> q_list;     // 队列,底层用 list

3. stack:后进先出

stack 可以理解为一个只开放了尾部操作的序列容器。

3.1 常用操作

函数功能
push(val)将元素压入栈顶
emplace(args...)在栈顶原地构造元素,避免临时对象
pop()移除栈顶元素(不返回该元素)
top()返回栈顶元素的引用(不删除)
empty()判断栈是否为空
size()返回栈中元素个数

重要提醒pop() 不返回值,这是因为为了保证异常安全,C++ 将“获取栈顶”和“移除栈顶”分成了两个操作。你必须先用 top() 取值,再 pop() 移除。

3.2 简单示例

#include <iostream>
#include <stack>

int main() {
    std::stack<int> st;
    
    // 压入元素
    st.push(10);
    st.push(20);
    st.push(30);
    
    std::cout << "栈大小:" << st.size() << "\n";
    
    // 遍历栈?(stack 不支持迭代器,只能通过 top + pop 逐个取出)
    std::cout << "从栈顶开始取出:";
    while (!st.empty()) {
        std::cout << st.top() << " ";   // 先取栈顶
        st.pop();                       // 再移除
    }
    std::cout << "\n栈现在是空的吗?" << std::boolalpha << st.empty() << "\n";
    
    return 0;
}

输出:

栈大小:3
从栈顶开始取出:30 20 10 
栈现在是空的吗?true

可见最后进去的 30 最先出来,这就是 LIFO。


4. queue:先进先出

queue 像一个两端开口的通道,一端只进,另一端只出。

4.1 常用操作

函数功能
push(val)在队尾添加元素
emplace(args...)在队尾原地构造元素
pop()移除队首元素(不返回该元素)
front()返回队首元素的引用(不删除)
back()返回队尾元素的引用
empty()判断队列是否为空
size()返回队列中元素个数

注意:queue 有 front 和 back,但没有 topstack 有 top,没有 front/back。这是它们的核心区别。

4.2 简单示例

#include <iostream>
#include <queue>

int main() {
    std::queue<std::string> q;
    
    // 入队
    q.push("张三");
    q.push("李四");
    q.push("王五");
    
    std::cout << "队伍大小:" << q.size() << "\n";
    std::cout << "队首:" << q.front() << ",队尾:" << q.back() << "\n";
    
    // 出队处理
    std::cout << "开始服务:";
    while (!q.empty()) {
        std::cout << q.front() << " ";   // 服务当前队首
        q.pop();                         // 离开队伍
    }
    std::cout << "\n现在队伍空了吗?" << std::boolalpha << q.empty() << "\n";
    
    return 0;
}

输出:

队伍大小:3
队首:张三,队尾:王五
开始服务:张三 李四 王五 
现在队伍空了吗?true

先进来的“张三”先离开,后来的“王五”最后离开,这就是 FIFO。

5. stack 和 queue 的底层容器

默认情况下,stack 和 queue 都使用 std::deque 作为底层容器。deque 是一种双端队列,两端增删都是 O(1),且支持随机访问(虽然适配器不用它)。

为什么默认选 deque 而不是 vector 或 list

  • deque 在两端增删非常高效,不会像 vector 那样在尾部扩容时可能导致大搬迁(虽然 vector 尾插也是 O(1) 均摊,但有时代价较大),也不会像 list 那样占用过多指针内存且缓存不友好。

  • deque 在大多数实现中会分块存储,综合性能均衡。

6. 不能做什么(常见误区)

  1. 直接遍历
    stack 和 queue 都没有迭代器,不能使用范围 for 循环,也不能用 [] 随机访问。如果你想遍历,只能一个个 top/front 后 pop,这会改变容器内容。如果想保留元素,必须用一个副本:

    std::stack<int> temp = st;   // 拷贝一份
    while (!temp.empty()) { /* 访问 temp.top() 并 pop */ }

  2. pop() 不返回值
    一定要先 top() 或 front() 取到值,再 pop()。写成一连串 pop() 只会删除元素,拿不到数据。

  3. 访问前不判断空
    对一个空栈调用 top() 或 pop() 是未定义行为,等价于操作无效内存。一定先检查 !empty()

  4. 混淆 back() 和 front()
    queue 的 back() 是最后加入的元素(队尾),front() 是最早加入的(队首)。不要用错。

7. 小结

场景推荐容器
后进先出,如函数调用栈、括号匹配、撤销操作stack
先进先出,如任务排队、消息缓冲、BFS 搜索queue
需要两端插入删除,且随机访问deque(直接使用)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值