跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++容器:stack、queue、deque 基本用法详解

C++ STL 标准模板库中的 stack、queue 和 deque 是高频使用的线性容器。deque 是原生序列容器,支持双端高效操作和随机访问;stack 和 queue 是基于 deque 实现的容器适配器,分别遵循后进先出和先进先出规则。文章详细讲解了它们的头文件引入、构造函数、核心成员函数、常见误区以及括号匹配、消息队列、滑动窗口等实战案例,帮助开发者根据场景选择合适的容器。

链路追踪发布于 2026/2/5更新于 2026/10/66.3K 浏览
C++容器:stack、queue、deque 基本用法详解

在 C++ STL 标准模板库中,stack(栈)、queue(队列)、deque(双端队列)是三组高频使用的线性容器,其中 stack 和 queue 并非「原生序列容器」,而是基于容器适配器实现的特殊数据结构,且它们的默认底层容器正是 deque。这三者各有独特的访问规则和适用场景,本文将从核心特性出发,逐步讲解它们的构造、常用操作及实战用法。

一、先理清三者的核心关系与基础认知

在正式学习用法前,先建立对这三个容器的整体认知,避免混淆核心概念:

  1. deque(双端队列):原生序列容器,底层是「分段连续内存」(并非完全连续,却能模拟连续内存的效果),支持双端高效插入/删除,也支持随机访问,是 stack 和 queue 的默认底层支撑容器。
  2. stack(栈):容器适配器,基于「后进先出(LIFO, Last In First Out)」规则工作,仅允许在容器的一端进行插入和删除操作,默认以 deque 为底层容器。
  3. queue(队列):容器适配器,基于「先进先出(FIFO, First In First Out)」规则工作,仅允许在容器的一端插入、另一端删除,默认同样以 deque 为底层容器。

简单比喻:

  • deque 像一个「两端都能进出的管道」;
  • stack 像一个「只有顶端能进出的瓶子」(后放进去的东西先拿出来);
  • queue 像一个「排队买票的队伍」(先排队的人先买票,后排队的人后买票)。

二、基础使用前提:头文件与命名空间

使用这三个容器前,必须包含对应的头文件,若想简化代码,可引入 std 命名空间(否则需显式使用 std:: 前缀):

// 分别包含对应头文件,按需引入
#include <deque> // deque 头文件
#include <stack> // stack 头文件
#include <queue> // queue 头文件
using namespace std;

三、deque(双端队列):双端高效操作的序列容器

deqeue 是「double-ended queue」的缩写,作为原生序列容器,它弥补了 vector(仅尾部高效)和 list(不支持随机访问)的部分短板,具备独特的优势。

3.1. deque 的核心特性

  • 底层:分段连续内存,通过中控器维护各段内存的指针,兼顾连续内存的访问效率和链表的内存灵活性。
  • 插入/删除:两端(头部、尾部)插入/删除效率极高,时间复杂度 O(1);中间插入/删除效率较低,时间复杂度 O(n)(需移动元素)。
  • 访问:支持随机访问(通过下标 [] 或 at()),时间复杂度 O(1),区别于 list 的双向迭代器。
  • 迭代器:支持随机访问迭代器(可进行 ++、--、+、- 操作,如 it+3),可正向、反向遍历。
  • 适用场景:需要频繁在两端操作元素,且偶尔需要随机访问的场景(如滑动窗口、缓存队列)。

3.2. deque 的常用构造函数(4 种高频方式)

和 list、vector 的构造方式高度相似,初学者可快速迁移知识:

// 1. 空构造(默认构造):创建一个空的 deque,存储 int 类型元素
deque<int> dq;
// 2. 填充构造:创建包含 n 个相同值的 deque
deque<int> dq1(5, 10); // 5 个元素,每个值为 10
deque<int> dq2(3);     // 3 个元素,每个值为 int 默认值 0
// 3. 迭代器范围构造:通过其他容器的迭代器范围复制构造
vector<int> vec = {1,2,3,4,5};
deque<int> dq3(vec.begin(), vec.end()); // 复制 vec 所有元素
deque<int> dq4(vec.begin(), vec.begin()+3); // 复制前 3 个元素
// 4. 拷贝构造:通过另一个 deque 复制构造
deque<int> dq5(5, 10);
deque<int> dq6(dq5); // dq6 与 dq5 完全一致

3.3. deque 的常用成员函数(核心操作)

deqeue 的成员函数兼具 vector 和 list 的部分特性,核心操作可分为「增、删、查、改、容器管理」五类:

(1)元素插入(双端 + 中间)
deque<int> dq;
// 1. 尾部插入:push_back(),O(1)
dq.push_back(10);
dq.push_back(20);
// 2. 头部插入:push_front(),O(1)(vector 无此函数)
dq.push_front(5);
dq.push_front(1);
// 3. 中间插入:insert(),O(n)(需移动元素)
// 迭代器指向第 2 个元素(值为 5),插入 15
deque<int>::iterator it = ++dq.begin();
dq.insert(it, 15);
// 此时 dq 内容:1 15 5 10 20
(2)元素删除(双端 + 中间)
deque<int> dq = {1,15,5,10,20};
// 1. 尾部删除:pop_back(),O(1),删除 20
dq.pop_back();
// 2. 头部删除:pop_front(),O(1),删除 1
dq.pop_front();
// 3. 中间删除:erase(),O(n)
// 删除迭代器指向的元素(15),返回下一个元素的迭代器
it = dq.begin();
it = dq.erase(it);
// 4. 清空所有元素:clear(),O(n)
dq.clear();
(3)元素访问与修改(随机访问 + 双端访问)

deqeue 支持两种访问方式,满足不同场景需求:

deque<int> dq = {1,2,3,4,5};
// 方式 1:随机访问(下标 [] 或 at()),O(1)
cout << "第 3 个元素(下标 2):" << dq[2] << endl; // 输出 3(无越界检查)
cout << "第 4 个元素(下标 3):" << dq.at(3) << endl; // 输出 4(有越界检查,更安全)
// 方式 2:双端访问(front() 头部、back() 尾部),O(1)
cout << "头部元素:" << dq.front() << endl; // 输出 1
cout << "尾部元素:" << dq.back() << endl; // 输出 5
// 修改元素(两种方式均可)
dq[2] = 30; // 下标修改,将 3 改为 30
dq.front() = 10; // 头部修改,将 1 改为 10
dq.back() = 50; // 尾部修改,将 5 改为 50
// 此时 dq 内容:10 2 30 4 50
(4)容器大小与状态管理
deque<int> dq = {1,2,3};
// 1. 获取元素个数:size(),O(1)
cout << "元素个数:" << dq.size() << endl; // 输出 3
// 2. 判断是否为空:empty(),O(1)
cout << "是否为空:" << dq.empty() << endl; // 输出 0(false)
// 3. 调整容器大小:resize()
dq.resize(5, 100); // 调整为 5 个元素,新增元素为 100,结果{1,2,3,100,100}
dq.resize(2);      // 调整为 2 个元素,删除多余元素,结果{1,2}
(5)迭代器遍历示例
#include <deque>
#include <iostream>
using namespace std;
int main() {
    deque<int> dq = {10,20,30,40,50};
    // 1. 正向遍历(随机访问迭代器)
    cout << "正向遍历:";
    for (deque<int>::iterator it = dq.begin(); it != dq.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
    // 2. 反向遍历
    cout << "反向遍历:";
    for (deque<int>::reverse_iterator it = dq.rbegin(); it != dq.rend(); it++) {
        cout << *it << " ";
    }
    cout << endl;
    // 3. 下标遍历(支持随机访问的优势)
    cout << "下标遍历:";
    for (int i = 0; i < dq.size(); i++) {
        cout << dq[i] << " ";
    }
    cout << endl;
    return 0;
}

输出结果:

文章配图

四、stack(栈):后进先出的容器适配器

stack 是一种「适配器容器」,它不直接实现数据存储,而是封装了底层容器(默认 deque),并提供专属的接口,强制限制只能在「栈顶」进行操作,符合后进先出的规则。

4.1. stack 的核心特性

  • 访问规则:仅允许在栈顶(容器的一端)进行插入、删除和访问操作,无法访问栈中间或栈底的元素。
  • 底层容器:默认使用 deque,也可指定 vector 或 list 作为底层容器(如 stack<int, vector<int>> st;)。
  • 无迭代器:不支持迭代器遍历,无法通过循环遍历所有元素(只能通过弹出栈顶元素的方式逐个获取)。
  • 时间复杂度:栈顶的插入(push)、删除(pop)、访问(top)操作均为 O(1)。
  • 适用场景:需要后进先出逻辑的场景(如括号匹配、函数调用栈、表达式求值、逆序输出数据)。

4.2. stack 的常用构造函数

stack 的构造方式较为简单,主要有两种:

// 1. 空构造(默认构造):创建一个空的栈,默认底层容器为 deque
stack<int> st;
// 2. 拷贝构造:通过另一个 stack 复制构造
stack<int> st1;
st1.push(10);
st1.push(20);
stack<int> st2(st1); // st2 与 st1 完全一致,包含 10、20(栈顶为 20)

4.3. stack 的常用成员函数(核心操作)

stack 的接口非常简洁,仅提供与栈顶相关的操作,无复杂逻辑,初学者容易掌握:

#include <stack>
#include <iostream>
using namespace std;
int main() {
    stack<int> st;
    // 1. 栈顶插入元素:push(elem),O(1)(压栈)
    st.push(10);
    st.push(20);
    st.push(30);
    // 此时栈内元素(从栈底到栈顶):10 → 20 → 30
    // 2. 访问栈顶元素:top(),O(1)(仅查看,不删除)
    cout << "当前栈顶元素:" << st.top() << endl; // 输出 30
    // 3. 删除栈顶元素:pop(),O(1)(出栈,无返回值,仅删除)
    st.pop(); // 删除 30,栈顶变为 20
    cout << "出栈后栈顶元素:" << st.top() << endl; // 输出 20
    // 4. 获取元素个数:size(),O(1)
    cout << "栈内元素个数:" << st.size() << endl; // 输出 2
    // 5. 判断栈是否为空:empty(),O(1)
    cout << "栈是否为空:" << st.empty() << endl; // 输出 0(false)
    // 6. 清空栈(stack 无 clear() 函数,需通过循环 pop() 实现)
    while (!st.empty()) {
        st.pop();
    }
    cout << "清空后栈是否为空:" << st.empty() << endl; // 输出 1(true)
    return 0;
}

输出结果:

文章配图

注意:

pop() 函数无返回值,仅删除栈顶元素,若需要获取栈顶元素,需先调用 top() 查看,再调用 pop() 删除。stack 没有 clear() 成员函数,清空栈需通过循环调用 pop(),直到 empty() 返回 true。当栈为空时,调用 top() 和 pop() 会触发未定义行为,建议操作前先用 empty() 判断。

五、queue(队列):先进先出的容器适配器

queue 同样是「适配器容器」,默认底层容器为 deque,它强制限制「一端插入、另一端删除」,符合先进先出的规则,类似日常生活中的排队场景。

5.1. queue 的核心特性

  • 访问规则:队尾(back)插入元素,队头(front)删除元素,仅能访问队头和队尾元素,无法访问队列中间元素。
  • 底层容器:默认使用 deque,也可指定 list 作为底层容器(如 queue<int, list<int>> q;,不推荐 vector,因为 vector 头部删除效率低)。
  • 无迭代器:不支持迭代器遍历,无法通过循环直接遍历所有元素(只能通过弹出队头元素的方式逐个获取)。
  • 时间复杂度:队尾插入(push)、队头删除(pop)、队头/队尾访问均为 O(1)。
  • 适用场景:需要先进先出逻辑的场景(如任务队列、消息队列、排队模拟、广度优先搜索(BFS))。

5.2. queue 的常用构造函数

和 stack 类似,queue 的构造方式也较为简洁:

// 1. 空构造(默认构造):创建一个空的队列,默认底层容器为 deque
queue<int> q;
// 2. 拷贝构造:通过另一个 queue 复制构造
queue<int> q1;
q1.push(10);
q1.push(20);
queue<int> q2(q1); // q2 与 q1 完全一致,包含 10、20(队头 10,队尾 20)

5.3. queue 的常用成员函数(核心操作)

queue 的接口同样简洁,仅提供与队头、队尾相关的操作,逻辑清晰:

#include <queue>
#include <iostream>
using namespace std;
int main() {
    queue<int> q;
    // 1. 队尾插入元素:push(elem),O(1)(入队)
    q.push(10);
    q.push(20);
    q.push(30);
    // 此时队列元素(从队头到队尾):10 → 20 → 30
    // 2. 访问队头元素:front(),O(1)(仅查看,不删除)
    cout << "当前队头元素:" << q.front() << endl; // 输出 10
    // 3. 访问队尾元素:back(),O(1)(仅查看,不删除)
    cout << "当前队尾元素:" << q.back() << endl; // 输出 30
    // 4. 删除队头元素:pop(),O(1)(出队,无返回值,仅删除)
    q.pop(); // 删除 10,队头变为 20
    cout << "出队后队头元素:" << q.front() << endl; // 输出 20
    // 5. 获取元素个数:size(),O(1)
    cout << "队列内元素个数:" << q.size() << endl; // 输出 2
    // 6. 判断队列是否为空:empty(),O(1)
    cout << "队列是否为空:" << q.empty() << endl; // 输出 0(false)
    // 7. 清空队列(queue 无 clear() 函数,需通过循环 pop() 实现)
    while (!q.empty()) {
        q.pop();
    }
    cout << "清空后队列是否为空:" << q.empty() << endl; // 输出 1(true)
    return 0;
}

输出结果:

文章配图

注意:pop() 函数无返回值,仅删除队头元素,若需要获取队头元素,需先调用 front() 查看,再调用 pop() 删除。queue 没有 clear() 成员函数,清空队列需通过循环调用 pop(),直到 empty() 返回 true。当队列为空时,调用 front()、back() 和 pop() 会触发未定义行为,操作前务必用 empty() 判断。

六、常见误区与注意事项

  1. 混淆「容器适配器」与「原生序列容器」:stack 和 queue 是适配器,不是原生容器,它们封装了底层容器(deque/vector/list),仅暴露有限的接口,不支持迭代器和随机访问;而 deque 是原生序列容器,接口更丰富,支持双端操作和随机访问。
  2. stack/queue 的遍历问题:两者均无迭代器,无法直接遍历,若需查看所有元素,只能通过「获取头部/栈顶元素→删除元素」的循环方式,且该方式会清空容器内的元素(若需保留数据,可先拷贝一份容器)。
  3. deque 与 vector、list 的选择:
    1. 需频繁随机访问、插入/删除多在尾部 → 选择 vector;
    2. 需频繁在中间插入/删除、无需随机访问 → 选择 list;
    3. 需频繁在两端插入/删除、偶尔随机访问 → 选择 deque。
  4. stack/queue 的底层容器替换:stack 可指定 vector/list 为底层容器,queue 可指定 list 为底层容器,但不推荐为 queue 指定 vector(vector 头部删除效率低,违背 queue 的设计初衷)。

七、实战案例:三者的典型应用

案例 1:stack 实现括号匹配(判断字符串中的括号是否成对有效)

#include <stack>
#include <iostream>
#include <string>
using namespace std;
// 判断括号是否有效匹配
bool isValidBracket(const string& s) {
    stack<char> st;
    for (char c : s) {
        // 左括号入栈
        if (c == '(' || c == '[' || c == '{') {
            st.push(c);
        } else {
            // 右括号:栈为空则无匹配的左括号,直接返回 false
            if (st.empty()) {
                return false;
            }
            // 取出栈顶左括号,判断是否与当前右括号匹配
            char topChar = st.top();
            st.pop();
            if ((c == ')' && topChar != '(') || (c == ']' && topChar != '[') || (c == '}' && topChar != '{')) {
                return false;
            }
        }
    }
    // 遍历结束后,栈为空说明所有括号都匹配完成
    return st.empty();
}
int main() {
    string s1 = "()[]{}";
    string s2 = "([)]";
    string s3 = "{[]}";
    cout << s1 << " 是否有效:" << (isValidBracket(s1) ? "是" : "否") << endl;
    cout << s2 << " 是否有效:" << (isValidBracket(s2) ? "是" : "否") << endl;
    cout << s3 << " 是否有效:" << (isValidBracket(s3) ? "是" : "否") << endl;
    return 0;
}

输出结果:

文章配图

案例 2:queue 实现简单消息队列(先进先出处理消息)

#include <queue>
#include <iostream>
#include <string>
using namespace std;
// 简单消息队列类
class MessageQueue {
private:
    queue<string> msgQueue; // 存储消息的 queue
public:
    // 发送消息(队尾入队)
    void sendMsg(const string& msg) {
        msgQueue.push(msg);
        cout << "发送消息:" << msg << endl;
    }
    // 接收消息(队头出队)
    void recvMsg() {
        if (msgQueue.empty()) {
            cout << "无未读消息!" << endl;
            return;
        }
        string msg = msgQueue.front();
        msgQueue.pop();
        cout << "接收消息:" << msg << endl;
    }
    // 查看未读消息数量
    int getUnreadCount() const {
        return msgQueue.size();
    }
};
int main() {
    MessageQueue mq;
    mq.sendMsg("Hello, C++ STL!");
    mq.sendMsg("这是一个队列消息");
    mq.sendMsg("消息处理完成~");
    cout << "未读消息数量:" << mq.getUnreadCount() << endl;
    mq.recvMsg();
    mq.recvMsg();
    mq.recvMsg();
    mq.recvMsg();
    return 0;
}

输出结果:

文章配图

案例 3:deque 实现滑动窗口(输出数组中每个窗口的最大值,简化版)

#include <deque>
#include <iostream>
#include <vector>
using namespace std;
// 滑动窗口:窗口大小为 k,输出每个窗口的最大值(简化版)
void slidingWindowMax(const vector<int>& nums, int k) {
    deque<int> dq; // 存储数组下标,维护窗口内的最大值下标
    for (int i = 0; i < nums.size(); i++) {
        // 1. 移除超出窗口范围的下标
        if (!dq.empty() && dq.front() < i - k + 1) {
            dq.pop_front();
        }
        // 2. 移除 deque 中比当前元素小的下标,保证 deque 头部是窗口最大值下标
        while (!dq.empty() && nums[dq.back()] < nums[i]) {
            dq.pop_back();
        }
        // 3. 当前下标入队
        dq.push_back(i);
        // 4. 窗口大小达到 k 时,输出最大值
        if (i >= k - 1) {
            cout << "窗口 [" << i - k + 1 << "," << i << "] 最大值:" << nums[dq.front()] << endl;
        }
    }
}
int main() {
    vector<int> nums = {1,3,-1,-3,5,3,6,7};
    int k = 3;
    slidingWindowMax(nums, k);
    return 0;
}

输出结果:

文章配图

八、三者核心特性对比表格

为了方便大家快速梳理和记忆,整理了 stack、queue、deque 的核心特性对比:

特性stack(栈)queue(队列)deque(双端队列)
数据结构类型容器适配器容器适配器原生序列容器
访问规则后进先出(LIFO),仅栈顶操作先进先出(FIFO),队尾入、队头出双端操作 + 随机访问
底层默认容器dequedeque-(自身为底层容器)
支持的核心操作位置栈顶队头(删、查)、队尾(插、查)头部、尾部、中间(随机访问)
迭代器支持不支持不支持支持随机访问迭代器
核心成员函数push()、pop()、top()push()、pop()、front()、back()push_front/back()、pop_front/back()、[]、at()
清空方式循环 pop()循环 pop()clear()(直接支持)
时间复杂度(核心操作)O(1)O(1)双端操作 O(1),随机访问 O(1)
典型适用场景括号匹配、函数调用栈任务队列、BFS、排队模拟滑动窗口、缓存队列、双端操作场景

总结

  1. deque 是原生双端队列,支持双端高效操作和随机访问,是 stack、queue 的默认底层容器,适合需要两端操作且偶尔随机访问的场景。
  2. stack 是后进先出适配器,仅支持栈顶操作,无迭代器,适合括号匹配、逆序输出等 LIFO 逻辑场景。
  3. queue 是先进先出适配器,支持队尾入、队头出,无迭代器,适合任务队列、BFS 等 FIFO 逻辑场景。
  4. 容器适配器(stack/queue)无 clear() 方法,需循环 pop() 清空,操作前务必用 empty() 判断是否为空,避免未定义行为。

对于 C++ 初学者而言,掌握这三个容器的核心用法,能灵活应对多数线性数据结构的应用场景,后续可结合更多实战案例,深化对 STL 容器的理解与运用。

目录

  1. 一、先理清三者的核心关系与基础认知
  2. 二、基础使用前提:头文件与命名空间
  3. 三、deque(双端队列):双端高效操作的序列容器
  4. 3.1. deque 的核心特性
  5. 3.2. deque 的常用构造函数(4 种高频方式)
  6. 3.3. deque 的常用成员函数(核心操作)
  7. (1)元素插入(双端 + 中间)
  8. (2)元素删除(双端 + 中间)
  9. (3)元素访问与修改(随机访问 + 双端访问)
  10. (4)容器大小与状态管理
  11. (5)迭代器遍历示例
  12. 四、stack(栈):后进先出的容器适配器
  13. 4.1. stack 的核心特性
  14. 4.2. stack 的常用构造函数
  15. 4.3. stack 的常用成员函数(核心操作)
  16. 五、queue(队列):先进先出的容器适配器
  17. 5.1. queue 的核心特性
  18. 5.2. queue 的常用构造函数
  19. 5.3. queue 的常用成员函数(核心操作)
  20. 六、常见误区与注意事项
  21. 七、实战案例:三者的典型应用
  22. 案例 1:stack 实现括号匹配(判断字符串中的括号是否成对有效)
  23. 案例 2:queue 实现简单消息队列(先进先出处理消息)
  24. 案例 3:deque 实现滑动窗口(输出数组中每个窗口的最大值,简化版)
  25. 八、三者核心特性对比表格
  26. 总结

更多推荐文章

查看全部
  • 零基础转行网络安全就业前景与技能要求分析
  • SpringBoot 整合 Flink CDC 实时追踪 MySQL 数据变动
  • 分布式文件存储服务设计与实现优化
  • 本地部署开源大模型教程:LangChain + Streamlit + Llama
  • 内网环境下搭建大模型微调平台实战指南
  • IDEA 中清理未使用的 JDK 版本
  • Java 处理 JSON 的实战技巧与性能优化
  • 哈希表核心实现:开放定址法与链地址法详解
  • C++ 手写高性能日志模块:基于策略模式实现
  • HarmonyOS 网络请求实战:Axios 集成与用户列表交互
  • MCP 模型上下文协议详解及在 IDE 与 Spring AI 中的应用
  • 7 款主流 AI 编程工具评测与选型指南
  • C++ list 容器源码实现及迭代器逻辑详解
  • 从零开始实现 LLaMa3 模型:核心架构与代码解析
  • 元境智搭:低代码可视化AR远程协助与巡检平台
  • OpenClaw 配置指南:定制专属 AI 助手
  • C# EmguCV 与 C++ OpenCV 轮廓检测性能对比分析
  • Python pip 国内镜像源配置指南:清华、阿里云等常用源设置
  • 基于STM32的全自动节水灌溉系统设计
  • Trae CN IDE Python 开发流程与配置指南

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online