数据结构栈

发布时间:2026/10/5 4:47:20

数据结构栈 1. 栈的基本概念1.1.1 概念栈Stack是一种限定仅在表的一端进行插入和删除操作的线性表。这一端称为栈顶top另一端称为栈底bottom。当栈中不包含任何元素时称为空栈。栈遵循后进先出LIFO, Last In First Out的原则最后入栈的元素最先被取出最先进栈的元素最后被取出。入栈Push将元素插入到栈顶的操作。出栈Pop从栈顶移除元素的操作。取栈顶元素Top获取栈顶元素但不删除它。这种特性使得栈在程序设计中具有广泛的应用场景如函数调用、表达式求值、括号匹配、递归实现等。1.1.2 入栈与出栈顺序分析以序列1, 2, 3为例若依次入栈则栈内状态变化如下操作栈状态从底到顶入栈1[1]入栈2[1, 2]入栈3[1, 2, 3]此时若连续出栈三次得到的结果为3, 2, 1—— 正好是逆序输出。⚠️ 关键点任意合法的入栈序列其对应的出栈序列必须满足“后进先出”的约束。例如无法通过合法操作使1, 3, 2成为出栈顺序因为 3 在 2 前面入栈却在 2 后面出栈违反了栈的 LIFO 特性。1.1.3 栈的基本操作接口定义以下是栈的核心接口函数原型适用于 C 语言实现// 栈的初始化voidStackInit(Stack*s);// 栈的销毁voidStackDestroy(Stack*s);// 元素入栈进栈voidStackPush(Stack*s,STDataType x);// 元素出栈并返回栈顶元素STDataTypeStackPop(Stack*s);// 获取栈顶元素不删除STDataTypeStackTop(Stack*s);// 获取栈中有效元素个数intStackSize(Stack*s);// 判断栈是否为空boolStackEmpty(Stack*s);这些接口构成了栈功能完整性的基础所有实现都围绕这组操作展开。1.2 栈的顺序存储结构栈的顺序存储基于数组实现分为静态顺序栈与动态顺序栈两种形式。1.2.1 静态顺序栈 vs 动态顺序栈类型特点适用场景静态顺序栈使用固定大小的数组空间不可变已知最大容量且不会超限的情况动态顺序栈使用malloc动态申请内存支持扩容大多数实际应用推荐使用❗ 静态顺序栈存在明显缺陷一旦数据量超过预设容量将导致溢出错误。因此在通用性要求高的系统中应优先采用动态顺序栈。1.2.2 动态顺序栈的实现核心代码typedefintSTDataType;// 可根据需要修改类型typedefstructStack{STDataType*data;// 存储栈元素的动态数组inttop;// 栈顶指针指向下一个可插入位置intcapacity;// 当前容量}Stack;// 初始化栈voidStackInit(Stack*s){assert(s!NULL);s-data(STDataType*)malloc(sizeof(STDataType)*4);// 初始容量为4if(s-dataNULL){printf(StackInit: 内存分配失败\n);exit(-1);}s-top0;s-capacity4;}// 销毁栈voidStackDestroy(Stack*s){assert(s!NULL);free(s-data);s-dataNULL;s-top0;s-capacity0;}// 扩容函数当栈满时自动扩展容量voidStackResize(Stack*s){assert(s!NULL);intnewCapacitys-capacity*2;STDataType*newData(STDataType*)realloc(s-data,sizeof(STDataType)*newCapacity);if(newDataNULL){printf(StackResize: 内存重分配失败\n);exit(-1);}s-datanewData;s-capacitynewCapacity;}// 入栈操作voidStackPush(Stack*s,STDataType x){assert(s!NULL);// 检查是否需要扩容if(s-tops-capacity){StackResize(s);}s-data[s-top]x;s-top;}// 出栈操作并返回栈顶元素STDataTypeStackPop(Stack*s){assert(s!NULL);assert(!StackEmpty(s));// 确保栈非空STDataType rets-data[--s-top];// 先减再取值returnret;}// 获取栈顶元素不删除STDataTypeStackTop(Stack*s){assert(s!NULL);assert(!StackEmpty(s));returns-data[s-top-1];}// 获取栈中元素个数intStackSize(Stack*s){assert(s!NULL);returns-top;}// 判断栈是否为空boolStackEmpty(Stack*s){assert(s!NULL);returns-top0;}✅关键优势时间复杂度所有操作均为O(1)O(1)O(1)除非触发扩容扩容策略采用加倍增长摊还分析表明平均每次插入成本仍为O(1)O(1)O(1)缓存友好连续内存布局提高 CPU 缓存命中率无内存碎片整个栈占用一块连续内存。1.3 栈的链式存储结构链式存储利用链表实现栈适合不确定元素数量或频繁动态增删的场景。1.3.1 链式栈结构设计要点推荐使用单链表实现必须将头节点作为栈顶即入栈为头插出栈为头删不建议使用带头结点的链表因为头插头删逻辑并无简化若使用双向链表虽可两端操作但额外开销大多一个指针性价比低。 结论单链表 不带头结点 头插头删 最优链式栈实现方案1.3.2 链式栈的结构体定义typedefintSTDataType;typedefstructListNode{STDataType data;structListNode*next;}LSNode;typedefstructLinkStack{LSNode*topHead;// 指向栈顶节点即链表头intsize;// 当前栈中元素个数}LinkStack;1.3.3 入栈操作头插法voidLinkStackPush(LinkStack*s,STDataType x){assert(s!NULL);LSNode*newNode(LSNode*)malloc(sizeof(LSNode));if(newNodeNULL){printf(LinkStackPush: 节点申请失败\n);exit(-1);}newNode-datax;newNode-nexts-topHead;// 新节点指向原栈顶s-topHeadnewNode;// 更新栈顶指针s-size;} 分析新元素插入至链表头部时间复杂度O(1)O(1)O(1)无需遍历。1.3.4 出栈操作头删法STDataTypeLinkStackPop(LinkStack*s){assert(s!NULL);assert(!LinkStackEmpty(s));LSNode*delNodes-topHead;STDataType topValdelNode-data;s-topHeaddelNode-next;// 移动栈顶指针free(delNode);// 释放旧节点s-size--;returntopVal;}✅ 优点出栈操作高效无需遍历缺点每个节点需额外内存开销指针域且不连续缓存性能差。1.4 栈的顺序存储与链式存储对比分析对比维度顺序存储动态数组链式存储单链表插入/删除时间复杂度O(1)O(1)O(1)均摊O(1)O(1)O(1)空间利用率高无额外指针开销低每个节点多一个指针内存连续性是连续存储否分散存储缓存命中率高局部性好低跳跃访问扩容代价一次性拷贝但频率低无实现复杂度中等较简单是否易发生内存碎片否是频繁申请释放推荐程度✅ 强烈推荐用于大多数场景仅在特定需求下使用结论在绝大多数情况下推荐使用动态顺序栈。其性能优越、内存效率高、缓存友好且实现清晰简洁。链式栈更适合教学演示或极端情况如无限增长且不允许扩容。1.5 实际应用示例括号匹配检测利用栈可以高效判断字符串中的括号是否匹配#includestdio.h#includeassert.hboolIsBracketMatch(constchar*str){Stack s;StackInit(s);for(inti0;str[i]!\0;i){charchstr[i];if(ch(||ch[||ch{){StackPush(s,ch);}elseif(ch)||ch]||ch}){if(StackEmpty(s)){StackDestroy(s);returnfalse;// 缺少左括号}chartopStackTop(s);if((ch)top!()||(ch]top![)||(ch}top!{)){StackDestroy(s);returnfalse;// 匹配失败}StackPop(s);}// 忽略其他字符}bool resultStackEmpty(s);StackDestroy(s);returnresult;}intmain(){constchar*test1(([]));constchar*test2([)];printf(%s - %s\n,test1,IsBracketMatch(test1)?匹配:不匹配);printf(%s - %s\n,test2,IsBracketMatch(test2)?匹配:不匹配);return0;}✅ 输出结果(([])) - 匹配 ([)] - 不匹配该例子充分体现了栈在解决嵌套结构验证问题上的强大能力。总结栈的核心价值与最佳实践核心思想后进先出LIFO限制操作位置提升效率。首选实现方式动态顺序栈兼顾性能与实用性。典型应用场景函数调用栈运行时堆栈表达式求值中缀转后缀括号匹配、路径回溯浏览器前进后退功能递归改写为迭代显式栈模拟学习建议掌握顺序栈的动态扩容机制理解栈在递归中的作用练习至少两个经典题目括号匹配、逆波兰表达式求值。✅ 本文内容已全面覆盖栈的基础理论、两种存储结构实现、性能对比及实战案例适合作为数据结构入门者的系统学习资料。
延伸阅读

更多相关文章

2026/10/5 4:42:20

STM32 LwIP网线插拔自动恢复:轮询与中断方案详解

说实话,这标题我太有共鸣了。搞过 STM32 联网项目的工程师基本都栽过同一个跟头:板子刚开始调通 LwIP 的时候,网线插上 ping 得通,拔了再插,十几秒后怎么 ping 都没反应。接着就是关电源重上电,网络又活了。…

2026/10/5 5:47:23

MFC下使用C++操作Word:COM自动化完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 5:47:23

MR25H40CDF MRAM与STM32F732IE工业存储方案实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 5:47:23

海思Hi3559A与CV100的DDR4硬件协同配置原理

1. 项目概述:为什么Hi3559A/CV100的DDR4配置不是“填参数”而是“调电路”你手头有一块海思Hi3559A或CV100的开发板,芯片手册翻到第387页,DDR4控制器寄存器表密密麻麻列了62个字段;你照着某份“通用配置模板”改完时序参数&#x…

2026/10/5 5:47:23

深入理解LSTM隐含层初始化:每个Batch为何都要重置状态?

这两个Batch没有可比性,因为它们的语义单元完全不同。Batch是训练过程中为了计算效率和梯度稳定性而划分的样本组,它是一个纯粹的“训练维度”概念;而时间步是序列本身的结构维度,是样本内部的先后关系。把两个维度混在一起讨论初…

2026/10/5 5:47:23

NXP S32K144上PMSM无感FOC实战调参指南

1. 这不是教科书,是我在NXP开发板上烧了7块PMSM驱动板后写下的实操笔记你搜“PMSM无感FOC”时,大概率会撞进一堆术语迷宫:AMCLIB、状态观测器、反电动势估算、PLL锁相环、初始位置检测……这些词堆在一起,像一堵密不透风的墙。我刚…

2026/10/5 5:42:22

景区人流与外卖订单共振:藏在假期生活大数据里的真实消费热度

景区人流与外卖订单共振:藏在假期生活大数据里的真实消费热度十月四日下午四点,黄金周的长假进度条已经悄无声息地滑过了中点。 工位旁的英短猫 Null 正趴在窗台边,全神贯注地盯着窗外一只在玻璃上停留的灰鸽子,两只圆耳朵随着鸽子…

2026/10/4 0:01:02

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/4 0:01:02

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/4 1:01:05

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑