C++ stack 全面解析与实战指南

C++ stack 全面解析与实战指南

在C++标准模板库(STL)中,stack(栈)是一种遵循“后进先出”(LIFO, Last In First Out)规则的容器适配器。它并非独立的容器,而是基于其他基础容器(如deque、vector、list)封装实现,屏蔽了部分基础容器的接口,仅暴露符合栈逻辑的操作。stack在日常开发中应用广泛,例如表达式求值、函数调用栈模拟、括号匹配等场景。本文将从stack的底层实现出发,详细讲解其核心特性、常用接口,结合实战案例演示具体用法,并梳理使用注意事项,帮助大家彻底掌握这一基础容器适配器。

一、stack 核心原理与特性

要理解stack的行为逻辑,首先需要明确其“容器适配器”的本质——它不直接管理内存,而是复用基础容器的内存管理和核心操作,仅对外提供统一的栈操作接口。

1.1 底层实现:基于基础容器的适配

stack的底层默认依赖deque容器实现(C++标准推荐,大部分编译器默认如此)。这是因为deque支持高效的尾部插入/删除操作(push_back、pop_back),且内存分配灵活,能很好地匹配栈的核心需求。同时,C++也支持指定其他符合要求的基础容器(需支持push_back、pop_back、back、empty、size这5个核心接口),例如vector、list等。

stack的适配逻辑非常简单:将栈的“压栈”对应基础容器的push_back(尾部插入),“出栈”对应基础容器的pop_back(尾部删除),“栈顶元素访问”对应基础容器的back(访问尾部元素)。通过这种适配,屏蔽了基础容器的头部操作、随机访问等接口,严格保证“后进先出”的规则。

1.2 核心特性总结

  • 后进先出(LIFO):最后插入的元素最先被访问,仅允许在栈顶进行插入和删除操作;

  • 容器适配器:不独立管理内存,依赖基础容器(默认deque)实现核心功能;

  • 接口受限:仅暴露栈相关接口(压栈、出栈、访问栈顶、判空、获取大小),不支持迭代器遍历,无法直接访问栈中间的元素;

  • 高效操作:压栈(push)、出栈(pop)、访问栈顶(top)的时间复杂度均为O(1)(依赖基础容器的尾部操作效率);

  • 无默认初始化元素:创建空stack时无默认元素,需手动压栈添加元素。

1.3 支持的基础容器

stack可指定的基础容器需满足“支持尾部插入/删除、访问尾部元素、判空、获取大小”这5个核心接口,STL中符合要求的容器有3个:

基础容器适配优势适用场景
deque(默认)尾部插入/删除效率高,内存分配灵活,无vector扩容时的大量数据拷贝开销大多数通用场景,推荐默认使用
vector内存连续,缓存命中率高,尾部操作效率稳定栈元素数量稳定,无需频繁扩容的场景
list尾部插入/删除效率高,无扩容开销,元素插入不会导致迭代器失效(但stack不支持迭代器)栈元素数量波动大,需频繁插入/删除的场景
指定基础容器的语法示例:
#include<stack>#include<vector>#include<list>// 基于vector的stackstd::stack<int,std::vector<int>>stack_vec;// 基于list的stackstd::stack<int,std::list<int>>stack_list;// 基于默认deque的stack(最常用)std::stack<int>stack_deque;}

二、C++ stack 常用接口详解

stack的接口设计简洁直观,仅包含与栈逻辑相关的核心操作。使用stack前,需包含头文件<stack>,并使用std命名空间(或显式指定std::stack)。

2.1 构造与析构

接口原型功能说明示例
stack();默认构造函数,创建空stack(基础容器也为空)std::stack s;
explicit stack(const Container& cont);用已有的基础容器对象cont初始化stack,stack的元素与cont一致std::deque d{1,2,3}; std::stack s(d);
stack(const stack& other);拷贝构造函数,创建一个与other内容完全相同的stackstd::stack s1; s1.push(1); std::stack s2(s1);
~stack();析构函数,释放基础容器的资源-

2.2 核心操作(压栈、出栈、访问栈顶)

这是stack最常用的接口,直接对应栈的核心逻辑:

接口功能说明注意事项时间复杂度
push(const value_type& val)将val压入栈顶(调用基础容器的push_back)val会被拷贝/移动到容器中O(1)
emplace(Args&&… args)在栈顶直接构造元素(调用基础容器的emplace_back)避免拷贝,效率高于pushO(1)
pop()删除栈顶元素(调用基础容器的pop_back)不返回被删除的元素;栈为空时调用会导致未定义行为O(1)
top()返回栈顶元素的引用(调用基础容器的back)栈为空时调用会导致未定义行为;可通过top()修改栈顶元素(若元素非const)O(1)

2.3 容量相关

接口功能说明示例
empty()判断栈是否为空(调用基础容器的empty),空返回true,否则返回falseif (s.empty()) { … }
size()返回栈中元素的个数(调用基础容器的size)cout << “栈大小:” << s.size();

2.4 赋值操作

接口原型功能说明示例
stack& operator=(const stack& other);拷贝赋值,将other的内容赋值给当前stack,覆盖原有内容s1 = s2; // s1的内容变为s2的内容
stack& operator=(stack&& other) noexcept;移动赋值,将other的内容移动到当前stack,other变为空s1 = std::move(s2); // 高效转移资源

2.5 接口综合示例

#include<stack>#include<iostream>usingnamespacestd;intmain(){// 1. 构造空stack(默认deque为基础容器)stack<int>s;// 2. 压栈操作s.push(10);s.push(20);s.emplace(30);// 直接构造,效率更高cout<<"栈大小:"<<s.size()<<endl;// 输出:3cout<<"栈顶元素:"<<s.top()<<endl;// 输出:30(最后压入的元素)// 3. 修改栈顶元素(非const情况下)s.top()=35;cout<<"修改后栈顶元素:"<<s.top()<<endl;// 输出:35// 4. 出栈操作s.pop();cout<<"出栈后栈顶元素:"<<s.top()<<endl;// 输出:20cout<<"出栈后栈大小:"<<s.size()<<endl;// 输出:2// 5. 判空与清空(stack无clear接口,需通过pop循环清空)while(!s.empty()){cout<<"出栈元素:"<<s.top()<<endl;s.pop();}cout<<"清空后栈是否为空:"<<(s.empty()?"是":"否")<<endl;// 输出:是// 6. 拷贝构造与赋值stack<int>s1;s1.push(1);s1.push(2);stack<int>s2(s1);// 拷贝构造cout<<"s2栈顶元素:"<<s2.top()<<endl;// 输出:2stack<int>s3;s3=s1;// 拷贝赋值cout<<"s3栈大小:"<<s3.size()<<endl;// 输出:2return0;}

输出结果:

栈大小:3 栈顶元素:30 修改后栈顶元素:35 出栈后栈顶元素:20 出栈后栈大小:2 出栈元素:20 出栈元素:10 清空后栈是否为空:是 s2栈顶元素:2 s3栈大小:2

三、stack 实战案例

stack的“后进先出”特性使其在多个经典场景中不可或缺,以下通过3个实战案例演示其实际应用:

3.1 场景1:括号匹配验证

需求:给定一个只包含括号(‘(’、‘)’、‘{’、‘}’、‘[’、‘]’)的字符串,判断字符串中的括号是否完全匹配(左右括号类型一致、顺序正确、无多余括号)。

思路:

  • 遍历字符串,遇到左括号(‘(’、‘{’、‘[’)时,将其压入栈中;

  • 遇到右括号时,判断栈是否为空(为空则无匹配的左括号,返回false),或栈顶左括号与当前右括号类型不匹配(返回false);若匹配,则弹出栈顶左括号;

  • 遍历结束后,栈需为空(否则存在未匹配的左括号,返回false),否则返回true。

#include<stack>#include<iostream>#include<string>#include<unordered_map>usingnamespacestd;boolisValid(string s){// 定义右括号到左括号的映射unordered_map<char,char>bracketMap={{')','('},{'}','{'},{']','['}};stack<char>st;for(charc:s){// 遇到右括号if(bracketMap.count(c)){// 栈为空或栈顶左括号不匹配if(st.empty()||st.top()!=bracketMap[c]){returnfalse;}// 匹配成功,弹出栈顶左括号st.pop();}else{// 遇到左括号,压入栈中st.push(c);}}// 遍历结束后栈需为空(无未匹配的左括号)returnst.empty();}intmain(){string s1="()[]{}";string s2="(]";string s3="([)]";string s4="{[]}";cout<<s1<<" 是否有效:"<<(isValid(s1)?"是":"否")<<endl;// 是cout<<s2<<" 是否有效:"<<(isValid(s2)?"是":"否")<<endl;// 否cout<<s3<<" 是否有效:"<<(isValid(s3)?"是":"否")<<endl;// 否cout<<s4<<" 是否有效:"<<(isValid(s4)?"是":"否")<<endl;// 是return0;}

3.2 场景2:逆波兰表达式求值

需求:逆波兰表达式(后缀表达式)是一种不含括号的表达式,运算符位于两个操作数之后,计算规则简单。给定一个逆波兰表达式的字符串数组,求其结果(假设表达式合法,仅包含数字和+、-、*、/四种运算符,除法向下取整)。

思路:

  • 遍历字符串数组,遇到数字时,将其转换为整数压入栈中;

  • 遇到运算符时,弹出栈顶两个元素(注意:先弹出的是右操作数,后弹出的是左操作数);

  • 计算两个操作数与运算符的结果,将结果压入栈中;

  • 遍历结束后,栈中仅剩一个元素,即为表达式的结果。

#include<stack>#include<iostream>#include<vector>#include<string>usingnamespacestd;intevalRPN(vector<string>&tokens){stack<int>st;for(conststring&token:tokens){// 遇到运算符if(token=="+"||token=="-"||token=="*"||token=="/"){// 弹出两个操作数(注意顺序:先弹右操作数,后弹左操作数)intright=st.top();st.pop();intleft=st.top();st.pop();// 计算结果并压栈if(token=="+"){st.push(left+right);}elseif(token=="-"){st.push(left-right);}elseif(token=="*"){st.push(left*right);}elseif(token=="/"){// 除法向下取整(C++中负数除法需注意,此处按题目要求处理)st.push(left/right);}}else{// 遇到数字,转换为整数压栈st.push(stoi(token));}}// 栈中仅剩结果returnst.top();}intmain(){vector<string>tokens1={"2","1","+","3","*"};// 等价于 (2+1)*3 = 9vector<string>tokens2={"4","13","5","/","+"};// 等价于 4 + (13/5) = 6vector<string>tokens3={"10","6","9","3","+","-11","*","/","*","17","+","5","+"};// 等价于 ((10*(6/(9+3*-11)))+17)+5 = 22cout<<"表达式1结果:"<<evalRPN(tokens1)<<endl;// 9cout<<"表达式2结果:"<<evalRPN(tokens2)<<endl;// 6cout<<"表达式3结果:"<<evalRPN(tokens3)<<endl;// 22return0;}

3.3 场景3:模拟函数调用栈

需求:模拟程序的函数调用过程,记录函数的调用顺序和返回顺序(函数调用时压栈,函数返回时出栈)。

#include<stack>#include<iostream>#include<string>usingnamespacestd;// 模拟函数调用(压栈)voidcallFunction(stack<string>&callStack,conststring&funcName){callStack.push(funcName);cout<<"调用函数:"<<funcName<<endl;}// 模拟函数返回(出栈)voidreturnFunction(stack<string>&callStack){if(callStack.empty()){cout<<"无正在执行的函数,无法返回!"<<endl;return;}string funcName=callStack.top();callStack.pop();cout<<"返回函数:"<<funcName<<endl;}intmain(){stack<string>callStack;// 模拟函数调用流程callFunction(callStack,"main()");callFunction(callStack,"funcA()");callFunction(callStack,"funcB()");returnFunction(callStack);// funcB返回callFunction(callStack,"funcC()");returnFunction(callStack);// funcC返回returnFunction(callStack);// funcA返回returnFunction(callStack);// main返回returnFunction(callStack);// 无函数可返回return0;}

输出结果:

调用函数:main() 调用函数:funcA() 调用函数:funcB() 返回函数:funcB() 调用函数:funcC() 返回函数:funcC() 返回函数:funcA() 返回函数:main() 无正在执行的函数,无法返回!

四、stack 使用注意事项

  1. 栈为空时禁止调用pop()和top():stack的pop()和top()接口在栈为空时调用会导致未定义行为(程序崩溃或异常)。因此,在调用这两个接口前,必须通过empty()判断栈是否为空。

  2. stack无迭代器,无法遍历:stack的设计初衷是严格遵循LIFO规则,因此未暴露迭代器接口,无法直接遍历栈中的所有元素。若需遍历,需通过pop()将元素依次弹出并记录(但会清空栈),或自定义基于基础容器的栈结构。

  3. stack无clear()接口,清空需手动循环pop():与vector、deque等容器不同,stack未提供clear()接口。若需清空栈,需通过while循环调用pop(),直到栈为空。

  4. 选择合适的基础容器

    • 默认的deque基础容器适用于大多数场景,兼顾效率和灵活性;

    • 若栈元素数量稳定,无需频繁扩容,可选择vector作为基础容器(内存连续,缓存命中率高);

    • 若需频繁插入/删除且元素数量波动大,可选择list作为基础容器(无扩容开销)。

  5. top()返回的是引用,可修改栈顶元素:stack的top()返回栈顶元素的非const引用,因此可以通过top()修改栈顶元素的值。若需禁止修改,可使用const stack。

  6. 元素类型需支持拷贝/移动:stack的push()接口会拷贝/移动元素到基础容器中,因此元素类型必须支持拷贝构造或移动构造。若元素为自定义类型,需确保正确实现拷贝/移动语义。

  7. 线程安全性:与所有STL容器一致,stack不保证线程安全。多线程环境下并发读写栈时,需手动加锁(如使用std::mutex)保护栈的操作。

五、总结

stack是C++ STL中基于基础容器封装的“后进先出”容器适配器,核心优势是接口简洁、操作高效(压栈、出栈、访问栈顶均为O(1))。它不独立管理内存,而是复用deque、vector、list等基础容器的功能,严格屏蔽了非LIFO相关的接口,确保数据操作的规范性。

stack的经典应用场景包括括号匹配、表达式求值、函数调用栈模拟等,掌握其核心接口和使用注意事项,能帮助我们快速解决这类“后进先出”相关的问题。使用时需注意:调用pop()和top()前先判空、无迭代器无法遍历、清空需手动循环pop()等细节,避免出现未定义行为。

希望本文能帮助大家彻底理解并掌握stack的使用,如果有疑问或补充,欢迎在评论区留言!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1157338.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MediaPipe Holistic深度解析:三合一模型的架构设计

MediaPipe Holistic深度解析&#xff1a;三合一模型的架构设计 1. 技术背景与核心挑战 在计算机视觉领域&#xff0c;人体感知一直是极具挑战性的任务。传统方法通常将人脸、手势和姿态作为独立模块处理&#xff0c;分别训练和部署模型。这种方式虽然实现简单&#xff0c;但在…

中文用户福音:IndexTTS2支持微信技术支持通道

中文用户福音&#xff1a;IndexTTS2支持微信技术支持通道 1. 引言 1.1 背景与痛点 在中文语音合成领域&#xff0c;高质量、富有情感表现力的文本转语音&#xff08;TTS&#xff09;系统长期面临两大挑战&#xff1a;一是技术门槛高&#xff0c;部署复杂&#xff1b;二是社区…

Holistic Tracking入门必看:543点检测数据格式详解

Holistic Tracking入门必看&#xff1a;543点检测数据格式详解 1. 引言&#xff1a;AI 全身全息感知的技术演进 随着虚拟现实、数字人和元宇宙应用的兴起&#xff0c;对全维度人体动作捕捉的需求日益增长。传统方案往往依赖多模型串联——先识别人体姿态&#xff0c;再单独处…

OpenCore Simplify 完整使用教程:轻松构建完美黑苹果系统

OpenCore Simplify 完整使用教程&#xff1a;轻松构建完美黑苹果系统 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify OpenCore Simplify 是一款专为黑…

Windows 11卡顿急救秘籍:三招让你的系统高效如初

Windows 11卡顿急救秘籍&#xff1a;三招让你的系统高效如初 【免费下载链接】Win11Debloat 一个简单的PowerShell脚本&#xff0c;用于从Windows中移除预装的无用软件&#xff0c;禁用遥测&#xff0c;从Windows搜索中移除Bing&#xff0c;以及执行各种其他更改以简化和改善你…

AI全息感知实战:基于Holistic Tracking的智能安防监控

AI全息感知实战&#xff1a;基于Holistic Tracking的智能安防监控 1. 技术背景与应用价值 随着人工智能在计算机视觉领域的持续突破&#xff0c;传统安防监控系统正从“看得见”向“看得懂”演进。传统的视频分析多聚焦于目标检测、行为识别等单一任务&#xff0c;难以实现对…

科哥微信技术支持!IndexTTS2使用中问题快速解决

科哥微信技术支持&#xff01;IndexTTS2使用中问题快速解决 1. 引言&#xff1a;本地化语音合成的工程挑战与支持闭环 在AI语音技术快速发展的今天&#xff0c;高质量、低延迟、可私有化部署的文本转语音&#xff08;TTS&#xff09;系统正成为智能应用的核心组件。IndexTTS2…

猫抓浏览器插件:零基础3分钟掌握全网资源嗅探技巧

猫抓浏览器插件&#xff1a;零基础3分钟掌握全网资源嗅探技巧 【免费下载链接】cat-catch 猫抓 chrome资源嗅探扩展 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 在网络冲浪的日常中&#xff0c;你是否曾遇到过心仪的视频无法下载&#xff1f;或者想要…

网页资源嗅探工具使用指南:轻松获取在线媒体内容

网页资源嗅探工具使用指南&#xff1a;轻松获取在线媒体内容 【免费下载链接】cat-catch 猫抓 chrome资源嗅探扩展 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 你是否曾经遇到过这样的情况&#xff1a;看到精彩的在线视频却无法保存&#xff0c;听到好…

如何让AI说话更自然?IndexTTS2情感调节实测

如何让AI说话更自然&#xff1f;IndexTTS2情感调节实测 在语音合成技术快速发展的今天&#xff0c;用户对TTS&#xff08;Text-to-Speech&#xff09;系统的要求早已超越“能说”&#xff0c;转向“说得像人”。尤其是在有声书、虚拟主播、智能客服等场景中&#xff0c;情感表…

BiliTools:2026年最强B站资源下载终极方案

BiliTools&#xff1a;2026年最强B站资源下载终极方案 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱&#xff0c;支持视频、音乐、番剧、课程下载……持续更新 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

终极Win11系统优化指南:一键清理冗余组件

终极Win11系统优化指南&#xff1a;一键清理冗余组件 【免费下载链接】Win11Debloat 一个简单的PowerShell脚本&#xff0c;用于从Windows中移除预装的无用软件&#xff0c;禁用遥测&#xff0c;从Windows搜索中移除Bing&#xff0c;以及执行各种其他更改以简化和改善你的Windo…

专业级网页视频下载解决方案:猫抓工具完整技术解析

专业级网页视频下载解决方案&#xff1a;猫抓工具完整技术解析 【免费下载链接】cat-catch 猫抓 chrome资源嗅探扩展 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 在现代数字内容消费中&#xff0c;网页视频已成为获取信息的主要渠道。然而&#xff0c…

OpCore Simplify:黑苹果EFI一键生成工具完全指南

OpCore Simplify&#xff1a;黑苹果EFI一键生成工具完全指南 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 还在为繁琐的黑苹果配置过程感到困惑吗&a…

Windows 11优化革命性指南:解决系统卡顿的高效策略

Windows 11优化革命性指南&#xff1a;解决系统卡顿的高效策略 【免费下载链接】Win11Debloat 一个简单的PowerShell脚本&#xff0c;用于从Windows中移除预装的无用软件&#xff0c;禁用遥测&#xff0c;从Windows搜索中移除Bing&#xff0c;以及执行各种其他更改以简化和改善…

版权要注意!使用IndexTTS2时参考音频合规建议

版权要注意&#xff01;使用IndexTTS2时参考音频合规建议 1. 引言&#xff1a;技术便利背后的法律边界 随着深度学习在语音合成领域的广泛应用&#xff0c;像 IndexTTS2 这类具备高自然度和情感控制能力的本地化TTS系统正逐步走入开发者、内容创作者乃至教育与医疗辅助等实际…

OpCore Simplify实战指南:智能EFI构建如何解决Hackintosh核心痛点

OpCore Simplify实战指南&#xff1a;智能EFI构建如何解决Hackintosh核心痛点 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 还在为复杂的OpenCore E…

BiliTools AI视频总结完整指南:3分钟高效掌握B站内容精华

BiliTools AI视频总结完整指南&#xff1a;3分钟高效掌握B站内容精华 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱&#xff0c;支持视频、音乐、番剧、课程下载……持续更新 项目地址: https://gitcode.com/GitHub_Trending/bilit/B…

Windows系统优化终极指南:一键清理释放15GB存储空间

Windows系统优化终极指南&#xff1a;一键清理释放15GB存储空间 【免费下载链接】Win11Debloat 一个简单的PowerShell脚本&#xff0c;用于从Windows中移除预装的无用软件&#xff0c;禁用遥测&#xff0c;从Windows搜索中移除Bing&#xff0c;以及执行各种其他更改以简化和改善…

突破认知边界的5种B站AI视频总结实战技法

突破认知边界的5种B站AI视频总结实战技法 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱&#xff0c;支持视频、音乐、番剧、课程下载……持续更新 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools 你是否曾经在…