news 2026/1/15 11:40:27

栈的深度解析与C++实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
栈的深度解析与C++实现

栈的深度解析与C++实现

一、什么是栈?

栈(Stack)是一种遵循**后进先出(LIFO, Last In First Out)**原则的线性数据结构。想象一下往木桶里放苹果,最后放进去的苹果,会最先被取出来——栈的操作逻辑就和这个场景完全一致。

栈有两个核心操作:

  • 入栈(Push):将元素添加到栈的顶端(栈顶);

  • 出栈(Pop):将栈顶的元素移除;

此外,栈通常还会提供top()操作(获取栈顶元素,不删除)、empty()操作(判断栈是否为空)、size()操作(获取栈中元素个数)等辅助接口。

栈的应用场景非常广泛,比如:

  • 函数调用栈:保存函数调用时的上下文信息;

  • 表达式求值:处理四则运算、括号匹配等;

  • 回溯算法:比如迷宫求解、子集问题等;

二、栈的C++实现方案

在C++中,实现栈主要有两种方式:

  1. 基于数组(静态数组/动态数组)实现;

  2. 基于链表实现;

其中,基于动态数组(vector)的实现方式最为高效且易用,因为vector的尾插(push_back)和尾删(pop_back)操作都是O(1)时间复杂度,完美匹配栈的入栈和出栈需求。下面我们将重点实现这种方式,同时也会简单介绍链表实现的思路。

2.1 基于vector的栈实现

我们将封装一个模板类Stack,支持任意数据类型的存储,核心接口包括:push、pop、top、empty、size。

完整代码实现
#include<iostream>#include<vector>#include<stdexcept>// 用于抛出异常// 栈的模板类实现(基于vector)template<typenameT>classStack{private:std::vector<T>data;// 用vector存储栈元素public:// 入栈:将元素添加到栈顶voidpush(constT&val){data.push_back(val);}// 出栈:移除栈顶元素(注意:空栈出栈需处理)voidpop(){if(empty()){throwstd::runtime_error("Stack is empty, cannot pop!");}data.pop_back();}// 获取栈顶元素(空栈访问需处理)T&top(){if(empty()){throwstd::runtime_error("Stack is empty, no top element!");}returndata.back();}// 常量版本top,适配常量对象constT&top()const{if(empty()){throwstd::runtime_error("Stack is empty, no top element!");}returndata.back();}// 判断栈是否为空boolempty()const{returndata.empty();}// 获取栈中元素个数size_tsize()const{returndata.size();}// 清空栈voidclear(){data.clear();}};// 测试代码intmain(){try{Stack<int>intStack;// 测试入栈intStack.push(10);intStack.push(20);intStack.push(30);std::cout<<"栈的大小:"<<intStack.size()<<std::endl;// 输出3std::cout<<"栈顶元素:"<<intStack.top()<<std::endl;// 输出30// 测试出栈intStack.pop();std::cout<<"出栈后栈顶元素:"<<intStack.top()<<std::endl;// 输出20std::cout<<"出栈后栈的大小:"<<intStack.size()<<std::endl;// 输出2// 测试清空栈intStack.clear();std::cout<<"清空后栈是否为空:"<<(intStack.empty()?"是":"否")<<std::endl;// 输出是// 测试空栈出栈(会抛出异常)intStack.pop();}catch(conststd::exception&e){std::cerr<<"异常信息:"<<e.what()<<std::endl;// 输出"Stack is empty, cannot pop!"}// 测试字符串类型的栈Stack<std::string>strStack;strStack.push("Hello");strStack.push("Stack");std::cout<<"字符串栈顶元素:"<<strStack.top()<<std::endl;// 输出"Stack"return0;}
代码说明
  1. 模板类设计:使用模板template <typename T>让栈支持任意数据类型(int、string、自定义对象等);

  2. 底层存储:采用std::vector作为底层容器,利用其动态扩容特性,无需手动管理内存;

  3. 异常处理:为空栈调用pop()top()时,抛出std::runtime_error异常,避免程序崩溃;

  4. 常量安全:提供常量版本的top()方法,确保常量对象也能访问栈顶元素;

2.2 基于链表的栈实现(简单思路)

基于链表实现栈时,通常选择头插法实现入栈,头删法实现出栈(因为链表头部操作的时间复杂度是O(1))。核心思路:

  • 定义链表节点结构,包含数据域和指针域;

  • 栈类中维护一个指向链表头部的指针(栈顶指针);

  • 入栈:创建新节点,将新节点的指针指向当前栈顶,再更新栈顶指针为新节点;

  • 出栈:保存栈顶节点的指针,更新栈顶指针为下一个节点,删除保存的节点;

链表实现的优点是无需预先分配内存,缺点是需要额外存储指针域,且实现稍显繁琐。如果对内存灵活性要求不高,优先选择基于vector的实现。

三、C++标准库中的栈(std::stack)

C++标准库已经为我们提供了栈的实现——std::stack,它定义在<stack>头文件中。std::stack是一个容器适配器,默认基于std::deque实现(也可以指定为vector或list)。

3.1 std::stack的基本使用
#include<iostream>#include<stack>#include<vector>intmain(){// 默认基于deque的栈std::stack<int>s1;s1.push(1);s1.push(2);std::cout<<"s1栈顶:"<<s1.top()<<std::endl;// 2s1.pop();std::cout<<"s1栈顶:"<<s1.top()<<std::endl;// 1// 指定基于vector的栈std::stack<int,std::vector<int>>s2;s2.push(10);s2.push(20);std::cout<<"s2栈大小:"<<s2.size()<<std::endl;// 2return0;}
3.2 std::stack的接口说明
  • push(const T& val):入栈;

  • pop():出栈(无返回值,若需获取栈顶元素,需先调用top());

  • top():获取栈顶元素;

  • empty():判断栈是否为空;

  • size():获取栈的大小;

注意:std::stack不支持直接遍历,这是因为栈的设计初衷就是限制访问方式,只允许操作栈顶元素,符合LIFO原则。

四、栈的常见问题实战

问题1:括号匹配问题

题目:给定一个只包含 ‘(’、‘)’、‘{’、‘}’、‘[’、‘]’ 的字符串,判断字符串是否有效(有效括号需满足:左括号必须用相同类型的右括号闭合,左括号必须以正确的顺序闭合)。

思路:利用栈的LIFO特性,遍历字符串时,遇到左括号则入栈;遇到右括号时,判断栈顶元素是否为对应的左括号,若是则出栈,否则无效。遍历结束后,栈必须为空(所有左括号都被匹配)。

基于std::stack的实现代码:

#include<iostream>#include<stack>#include<unordered_map>#include<string>boolisValid(std::string s){std::stack<char>st;// 存储右括号对应的左括号std::unordered_map<char,char>map={{')','('},{'}','{'},{']','['}};for(charc:s){if(map.count(c)){// 遇到右括号if(st.empty()||st.top()!=map[c]){// 栈空或不匹配returnfalse;}st.pop();// 匹配成功,出栈}else{// 遇到左括号,入栈st.push(c);}}returnst.empty();// 所有左括号必须被匹配}intmain(){std::cout<<isValid("()")<<std::endl;// 1(有效)std::cout<<isValid("()[]{}")<<std::endl;// 1(有效)std::cout<<isValid("(]")<<std::endl;// 0(无效)std::cout<<isValid("([)]")<<std::endl;// 0(无效)std::cout<<isValid("{[]}")<<std::endl;// 1(有效)return0;}

五、总结

  1. 栈是遵循LIFO原则的线性数据结构,核心操作是入栈和出栈,时间复杂度均为O(1);

  2. C++中实现栈有两种常用方式:基于vector(高效易用)和基于链表(内存灵活);

  3. 标准库的std::stack是容器适配器,默认基于deque实现,可直接用于开发,无需重复造轮子;

  4. 栈的典型应用是括号匹配、函数调用、回溯算法等,核心思路是利用“后进先出”的特性暂存中间数据。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/2 3:40:35

数字电路实验:多路选择器设计全面讲解

多路选择器设计实战&#xff1a;从真值表到FPGA实现的完整路径你有没有遇到过这样的情况&#xff1f;在做数字电路实验时&#xff0c;明明仿真结果完全正确&#xff0c;可一烧录进开发板&#xff0c;输出就是不对劲——LED不亮、信号跳变毛刺满屏&#xff0c;甚至整个系统“死机…

作者头像 李华
网站建设 2026/1/9 22:50:12

从零实现:在Windows构建virtual serial port driver测试环境

从零搭建 Windows 虚拟串口驱动测试环境&#xff1a;实战全解析你有没有遇到过这样的场景&#xff1f;调试一个嵌入式设备时&#xff0c;手边只有两三个物理 COM 口&#xff0c;却要同时模拟主从机通信&#xff1b;写了个串口协议解析器&#xff0c;但没有真实硬件可用&#xf…

作者头像 李华
网站建设 2026/1/2 3:36:47

基于Python+Django+SSM出行路线规划与推荐系统(源码+LW+调试文档+讲解等)/出行路线规划/路线推荐系统/出行推荐系统/路线规划系统/智能出行路线规划/出行路线智能推荐/路线规划与推荐

博主介绍 &#x1f497;博主介绍&#xff1a;✌全栈领域优质创作者&#xff0c;专注于Java、小程序、Python技术领域和计算机毕业项目实战✌&#x1f497; &#x1f447;&#x1f3fb; 精彩专栏 推荐订阅&#x1f447;&#x1f3fb; 2025-2026年最新1000个热门Java毕业设计选题…

作者头像 李华
网站建设 2026/1/2 3:36:25

DUT测试程序开发基础:零基础入门指引

DUT测试程序开发实战入门&#xff1a;从零搭建你的第一个自动化测试系统你有没有遇到过这样的场景&#xff1f;手头有一块新设计的电路板&#xff0c;或者一颗刚流片回来的芯片&#xff0c;想验证它能不能正常工作。最原始的办法是拿万用表一个个测电压、用示波器看波形——但这…

作者头像 李华
网站建设 2026/1/12 1:36:00

Windows能运行CosyVoice3吗?需通过WSL或虚拟机实现

Windows 能运行 CosyVoice3 吗&#xff1f;WSL 与虚拟机的实战部署指南 在生成式 AI 浪潮席卷各行各业的今天&#xff0c;语音合成技术早已不再是实验室里的“黑科技”。阿里开源的 CosyVoice3 正是这一趋势下的明星项目——它不仅能用 3 秒音频克隆人声&#xff0c;还能通过自…

作者头像 李华
网站建设 2026/1/2 3:34:26

快速理解MDK驱动开发中的链接脚本配置方法

掌握MDK链接脚本&#xff1a;从内存布局到实战配置的深度指南 在嵌入式开发的世界里&#xff0c;一个项目能否稳定运行&#xff0c;往往不只取决于代码逻辑是否正确&#xff0c;更关键的是—— 你的程序有没有被“放”在对的地方 。 当你按下下载按钮&#xff0c;MDK&#x…

作者头像 李华