跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
C++算法

C++ STL 栈与队列模拟实现及容器适配器原理

C++ STL 栈与队列作为容器适配器,底层依赖 deque、vector 或 list。解析其设计原理,通过最小栈、二叉树层序遍历等算法题演示先进后出与先进先出特性,展示基于模板的模拟实现代码,帮助理解容器适配机制及内存管理细节。

虚拟内存发布于 2026/3/24更新于 2026/5/68 浏览
C++ STL 栈与队列模拟实现及容器适配器原理

C++ STL 栈与队列模拟实现及容器适配器原理

前言

本文从 STL 容器适配器视角,深度解析栈与队列的设计本质——以双端队列(deque)为底层容器,实现高效头尾操作。结合最小栈、二叉树层序遍历等经典算法题,实战演示栈的"先进后出"与队列的"先进先出"特性,并揭秘优先级队列的堆实现原理。

一、栈与队列原型简介

1.1 Stack

Stack 官方文档链接: https://cplusplus.com/reference/stack/stack/?kw=stack

在 Stack 官方文档说明中介绍其不是容器而是容器适配器。在文档中栈和在 C 语言中实现的其——先进后出的特点,其使用的是模板 + 封装实现的,这里使用的缺省参数是 deque(在后面我们会介绍),如下是栈的主要接口:

这些接口我们发现很熟悉,在 C 语言中便已经实现过了。

注意:容器适配器不再拥有迭代器了,举个简单的例子,如果栈和队列拥有了迭代器便不能保证先进先出和先进后出,而是可以通过迭代器的下标 + [] 即可出入数据。

1.2 Queue

队列官方文档链接: https://cplusplus.com/reference/queue/queue/

同理:在 Queue 官方文档说明中介绍其不是容器而是容器适配器,在文档中栈和在 C 语言中实现的其——先进先出的特点,如下是队列的主要接口:

1.3 最小栈的练习

力扣网址: https://leetcode.cn/problems/min-stack/

题目解释: 在本题中除了需要完成栈的基本接口以外,还要在时间复杂度为 O(1) 的情况下找到最小的元素。

思路: 在这道题中大部分想到的思路便是:

  1. 通过定义变量来存储最小的值,将第一次插入到栈的数据先存放到变量中(假设第一次插入的数据为最小值)。
  2. 再将后续的数据一个个插入到栈中,依次和变量存储的数据进行比较,如果插入栈中的数据小于变量存放的则变量中的数据更新为新的数据。

但是我们如果要出栈的话,存放在变量中的最小元素也要删除,那么在栈中最小的元素是什么?如果我们要遍历栈,那么时间复杂度为 O(n)。

在前面的算法中我们便知道可以定义两个栈,一个栈 _str 用来进行入栈和出栈数据的存放与删除,一个栈 _minstr 用来和 _str 中插入的数据比大小:

  • 若 _str 入的数据小于 _minstr 中的数据,那么便将该数据插入到 _minstr 中(第一次数据入 _str 的同时也要入 _minstr)。
  • 如果 _str 插入的数据一直大于 _minstr 存放的数据,那么便不在 _minstr 中入数据(如果插入的数据与 _minstr 存放的数据相同是要将该数据存放到 _minstr 中)。

代码演示:

class MinStack {
public:
    MinStack() {}
      { 
        _str.(val);
         (_minstr.() || val <= _minstr.()) { 
            _minstr.(val); 
        }
    }
    {
         (_str.() == _minstr.()) { 
            _minstr.(); 
        } 
        _str.();
    }
    {  _str.(); }
    {  _minstr.(); }
: 
    stack<> _str; 
    stack<> _minstr;
};
void
push
(int val)
push
if
empty
top
push
void pop()
if
top
top
pop
pop
int top()
return
top
int getMin()
return
top
private
int
int

1.4 栈的压入、弹出序列

牛客网址: 栈的压入、弹出序列

题目解释: 给定两个数组,第一个表示进栈的顺序,第二个数组表示出栈的顺序,判断两个数组是否为一个栈的出入栈的顺序。

思路: 只要入栈序列能模拟出出栈序列的过程,那么就是匹配的,否则不是。

  1. 入栈序列先入栈(每次入一个数据)。
  2. 每次入栈完后将该数据和出栈序列的数据进行比对,如果匹配则持续出数据(出栈序列指针依次往后走),不匹配则继续将入栈序列的数据入栈(入栈序列指针往后走)。
  3. 入栈序列结束则结束。

代码实现:

class Solution {
public:
    bool IsPopOrder(vector<int>& pushV, vector<int>& popV) {
        size_t popi = 0;
        stack<int> st;
        for (auto e : pushV) {
            st.push(e);
            while (!st.empty() && st.top() == popV[popi]) {
                st.pop();
                popi++;
            }
        }
        return st.empty();
    }
};

1.5 二叉树的层序遍历

力扣网址: 二叉树的层序遍历

题目解释: 要求将二叉树的每一层放到二维数组的每一行上组成二维数组,这里的二维数组的每一行便不再相等(不是标准的二维数组)。

那么怎么将输入的二叉树的节点放到二维数组对应的位置上?

思路: 广度优先遍历。

  1. 先将二叉树的根(3)入到队列里面,然后将其出出来,再将其的孩子入进去(9, 20)。
  2. 将 9 出出来,再将 9 的孩子节点出来,因为 9 没有孩子节点,所以不用出。
  3. 将 20 出出来,再将 20 的孩子节点出出来。

但是如果 9 有孩子节点,那么队列中会同时存在两层不同的数据,那么我们怎么区分?

这里我们想到可以使用最小栈的思路,定义两个队列,第一个队列存放树的节点的指针,第二个队列存该数据是第几层的:

  1. 我们在 q1 队列入数的根节点(3),3 对应的是第一层,因此 q2 队列存放 1。
  2. 将 3 和 1 出队列,再将 3 的孩子 9, 20 入队列,因为根节点对应的是第一层,因此其孩子节点只需 ++,即第二层。
  3. 将 9 出出来,因为其没有孩子节点,所以 q2 不入数据,q1 出 20,将 20 的孩子节点 15, 7 入数据,15, 7 对应的是第三层。

但是由于定义了两个队列,有所繁琐,下面我们只使用一个队列看看如何搞定!

思路 2:定义变量 levelSize 代表当前层的数据个数,我们每一次出数据都将该层的数据全部出出来(levelSize 为 0 代表该层的数据出完了)。

  1. 将根节点 3 入队列,levelSize 为 1,将 3 出队列,孩子节点 9, 20 入队列,levelSize 更新为 2。
  2. 将 9 出队列 levelSize 变为 1,再将 20 出队列,levelsize 为 0,再将其孩子节点 15, 7 入队列,levelSize 更新为 2,因此队列的 size 就是下一层的数据个数。

代码实现:

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> vv;
        queue<TreeNode*> q;
        int levelsize = 0;
        if (root) {
            q.push(root);
            levelsize = 1;
        }
        while (!q.empty()) {
            vector<int> v;
            while (levelsize--) {
                TreeNode* front = q.front();
                q.pop();
                v.push_back(front->val);
                if (front->left) q.push(front->left);
                if (front->right) q.push(front->right);
            }
            vv.push_back(v);
            levelsize = q.size();
        }
        return vv;
    }
};

二、模拟实现栈

2.1 容器适配器

在传统实现容器时我们都使用模板,如下:

但在这里我们便使用适配器,那么什么是适配器?

适配器是一种设计模式,该种模式是将一个类的接口转换成客户希望的另外一个接口。

在日常生活中由于家里的供电标准是 120v,但是实际上我们手机或者电灯充电的是 3v/5v,那么为什么我们的设备没有因为电量过大导致炸了?这便是因为我们使用了充电器将电压转换成我们需要的功率。

2.2 栈的实现

在认识到适配器后,我们思考下栈的特点便是在栈顶插入删除元素这些操作,在我们已经学到的容器中 vector 和 list 支持在一段插入删除数据,因此其通过封装便可得到栈,这里我们传 Container(容器),好处便是栈的类型不再写死,而是可以传 vector/list 都可以,通过 Container 适配转换出要的 stack。

代码实现:

#include <vector>
#include <list>

namespace hxx {
    template<class T, class Container = vector<T>>
    class stack {
    public:
        void push(const T& x) { _con.push_back(x); }
        void pop() { _con.pop_back(); }
        const T& top() { return _con.back(); }
        size_t size() const { return _con.size(); }
        bool empty() const { return _con.empty(); }
    private:
        Container _con;
    };
}

三、模拟实现队列

队列的实现与栈的实现大同小异,但是我们需要注意队列的实现不能通过封装 vector 来适配出队列。

  1. 队列删除队头元素对应的是 pop_front,但是在 vector 中没有该接口。
  2. vector 头插和头删的效率低下。

代码实现:

namespace hxx {
    template<class T, class Container = list<T>>
    class queue {
    public:
        void push(const T& x) { _con.push_back(x); }
        void pop() { _con.pop_front(); }
        const T& front() { return _con.front(); }
        const T& back() { return _con.back(); }
        size_t size() const { return _con.size(); }
        bool empty() const { return _con.empty(); }
    private:
        Container _con;
    };
}

四、总结

希望读完本文可以帮助读者更好了解栈和队列的底层逻辑。

目录

  1. C++ STL 栈与队列模拟实现及容器适配器原理
  2. 前言
  3. 一、栈与队列原型简介
  4. 1.1 Stack
  5. 1.2 Queue
  6. 1.3 最小栈的练习
  7. 1.4 栈的压入、弹出序列
  8. 1.5 二叉树的层序遍历
  9. 二、模拟实现栈
  10. 2.1 容器适配器
  11. 2.2 栈的实现
  12. 三、模拟实现队列
  13. 四、总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Python 实现定时任务的八种常见方案
  • Chaterm:开源 AI 智能终端与 SSH 客户端功能解析
  • 大模型微调方法总结:从全量到参数高效微调
  • AIGC 时代大模型幻觉问题深度治理:技术体系、工程实践与未来演进
  • Clawdbot 部署 Qwen3:32B:解决 Token 过期前端无提示及 URL 刷新问题
  • AI 小说生成器本地部署与配置指南
  • LMArena.ai 全球 AI 模型盲测对战与排行榜使用指南
  • AI 大模型赋能专利翻译:核心功能与应用场景解析
  • OpenHarmony 与华为云 IoT 智能家居实战指南
  • 基于C++的嵌入式AI模块化架构设计全流程
  • Transformer 入门教程:原理、架构与实战详解
  • Qwen3-32B 本地部署:Clawdbot 网关与企业微信/钉钉集成实战
  • 微信小程序健康菜谱分享网站:Python 技术架构与实现
  • 验证回文串:双指针解法
  • OpenClaw 开源 AI 执行引擎:架构、安装与实战详解
  • MySQL 权限管理与 C/C++ 客户端对接实战
  • llamafile 使用指南:从下载到运行仅需 3 步
  • 现代C++与MASM协同开发实践:x64架构下的底层优化
  • Z-Image-Turbo 图片生成后自动转换 JPG/WEBP 方案
  • Qwen2 技术报告:模型性能与多语言能力解析

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如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