栈

栈
THEDI栈
栈的介绍
栈(Stack):也叫做
堆栈,一种遵循后进先出LIFO(Last In First Out)策略的线性数据结构。只允许在一端进行插入与删除操作,这一端称为栈顶(top)。常用于函数调用管理、表达式求值、括号匹配、回溯等场景。
优势
- 操作语义简单,接口小巧
- 入栈/出栈时间复杂度理想(O(1) 摊还/均摊)
- 适合实现回溯、撤销等场景
劣势
访问受限,仅能访问栈顶
顺序栈可能需要扩容策略;链栈每次操作都有分配/释放开销
我们可以把栈想象成一摞叠放的盘子:
- 栈顶(top):可以插入和删除元素的一端,就像盘子堆的最上面。
- 栈底(bottom):固定不动的一端,不能进行操作,就像盘子堆的最下面。
- 空栈:栈中没有任何元素时,称为空栈。
基本操作
栈的常见操作有:
入栈(Push):在栈顶加入一个新元素。
出栈(Pop):移除并返回栈顶的元素。
查看栈顶(Peek):只查看栈顶元素,但不移除。
下图展示了栈的结构和操作方式:
栈的实现方式
与线性表类似,栈常见的存储方式有两种:顺序栈 和 链式栈。
- 顺序栈:采用一段连续的存储空间(==数组==)依次存放从栈底到栈顶的元素,并通过
指针top标记当前栈顶元素在数组中的位置。 - 链式栈:采用==单链表==实现,每次新元素都插入到链表头部,
top始终指向链表的头节点,即栈顶元素的位置。
顺序栈(数组实现)
栈的最常见实现方式是利用数组来构建顺序存储结构。这种基于顺序存储的栈结构,通常被称为顺序栈。
静态顺序栈
栈结构体定义
1 | /** |
静态栈的实现不涉及动态扩容,只需要我们
一个固定大小的数组和一个栈顶指针即可构成Stack的结构体
API设计
1 | /* 基础管理 */ |
静态顺序栈源码实现
github源码:https://github.com/keqiudi/Stack/tree/master/Sequential_Stack/static_stack
static_stack.h
1 | /** |
static_stack.c
1 | // |
测试代码
1 |
|
输出结果如下:
1 | C:\Users\keqiu\myProjects\CLionProjects\C\Stack\Sequential_Stack\static_stack\build\Stack.exe |
动态顺序栈
栈结构体定义
1 | /** |
由于动态栈的实现种涉及到动态扩容,所以我们将
固定大小的数组换成了一个指向这块数据内存的首地址的指针和一个可以记录容量的变量我们仍然可以通过访问数组的方式访问这个指针,如:stack->data[stack->top-1]就是访问栈顶元素的数据
API设计
1 | /* 基础管理 */ |
动态顺序栈源码实现
github源码:https://github.com/keqiudi/Stack/tree/master/Sequential_Stack/dynamic_stack
dynamic_stack.h
1 | // stack.h |
dynamic_stack.c
1 | // |
测试代码
1 | // main.c |
输出结果如下:
1 | C:\Users\keqiu\myProjects\CLionProjects\C\Stack\Sequential_Stack\dynamic_stack\build\dynamic_stack.exe |
链式栈
静态顺序栈在存储空间上存在一定局限性:当栈满或需要扩容时,往往需要移动大量元素,效率较低。为了解决这一问题,可以采用链式存储结构实现栈
链式栈本质上就是一个链表,保存着栈顶指针top(链表头结点),每次入栈就是执行一次头插法插入元素
链式栈结构体定义
1 | /** 栈元素类型(可按需修改为自定义结构体/其他标量类型) */ |
链式栈的每个结点LStackNode由
结点数据域(data)和指向下一个结点的指针(struct LStackNode* next)构成链式栈LStack保存着
栈顶指针 top(链表头结点)和元素个数(size)
API设计
1 | /* 基础管理与状态 */ |
api设计与链表和顺序栈类似,只不过链式栈没有判断栈满的api
链式栈源码实现
github源码:Stack/Linked_Stack at master · keqiudi/Stack
linked_stack.h
1 | /** |
linked_stack.c
1 | // |
测试代码
1 |
|
输出结果:
1 | C:\Users\keqiu\myProjects\CLionProjects\C\Stack\Linked_Stack\build\Linked_Stack.exe |
顺序栈与链式栈对比
| 维度 | 静态顺序栈(固定数组) |
动态顺序栈(可扩容数组) |
链式栈(单链表) |
|---|---|---|---|
| 存储结构 | 连续内存(固定大小) | 连续内存(可增长) | 连续/非连续内存(结点+指针) |
| 容量/扩展 | 固定上限 | 扩容按倍增,偶发搬移 O(n) | 理论无限,受堆内存限制 |
| push/pop | O(1) | 均摊 O(1),扩容最坏 O(n) | O(1),含 malloc/free |
| 空间开销 | 最小 | 可能有预留空闲 | 每元素多一个指针,含堆管理开销 |
| 是否需要手动内存分配 | 否(编译期/一次性固定空间) | 是(malloc/realloc/free) | 是(每次入/出栈涉及 malloc/free,或用内存池) |
| 典型场景 | 上限已知、稳定性要求高、嵌入式 | 上限不明、需弹性且性能稳定 | 规模波动大、无需连续内存 |
| 核心优点 | 简单、快速、可预估 | 弹性好、总体性能佳 | 按需分配、无整体扩容 |
| 核心缺点 | 上限刚性、可能浪费 | 扩容可能致指针失效、搬移代价 | 碎片化、分配释放开销 |









