栈
·本篇:1.5k字 大约需要: 6分钟
前言
栈的实现及相关操作
编程语言:C
栈
栈的抽象数据类型
栈的定义
栈是限定仅在表尾进行插入和删除操作的线性表
- 允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom),不含任何数据元素的栈称为空栈
- 栈又称为后进先出(Last In First Out)的线性表,简称LIFO结构
- 栈的插入操作叫作进栈,也称压栈、入栈
- 栈的删除操作,叫作出栈,也有的叫作弹栈
基本操作的实现
- 结构代码
1 |
|
- 初始化一个空栈
1 | void InitSqStack(SqStack *S) |
- 判断栈是否已满
1 | bool Full(SqStack *S) |
- 判断栈是否为空
1 | bool Empty(SqStack *S) |
- 向栈中插入元素
1 | void Push(SqStack *S, int target) |
- 删除栈顶元素
1 | void Pop(SqStack *S) |
- 将栈清空
1 | void ClearStack(SqStack *S) |
- 返回栈顶元素
1 | int GetTop(SqStack *S) |
- 返回栈的元素个数
1 | int StackLength(SqStack *S) |
- 遍历栈中的所有元素
1 | void DisplayStack(SqStack *S) |
两栈共享空间
我们可以用一个数组来存储两个类型相同的栈
数组有两个端点,两个栈有两个栈底,让一个栈的栈底为数组的始端,即下标为0处,另一个栈为数组的末端,即下标为数组长度n - 1处,这样两个栈如果增加元素,就是两端点向中间延伸
代码实现
- 结构代码
1 |
|
- 初始化两栈空间
1 | void InitSqDStack(SqDoubleStack *S) |
- 入栈
1 | void Push(SqDoubleStack *S, int target, int stackNumber) // 判断是栈1还是栈2的参数stackNumber |
- 出栈
1 | void Pop(SqDoubleStack *S, int *target, int stackNumber) // 用target返回出栈的值 |
- 遍历
1 | void Display(SqDoubleStack *S, int stackNumber) |
栈的链式存储结构及实现
栈的链式存储结构,简称为链栈
将栈顶放在单链表的头部,通常对于链栈来说,是不需要头结点的
那么链栈的空就是top = NULL
代码实现
- 结构代码
1 |
|
- 初始化链栈
1 | void InitLinkStack(LinkStack *L) |
- 入栈
1 | void Push(LinkStack *L, int target) |
- 出栈
1 | void Pop(LinkStack *L, int *target) // 用target保存出栈元素 |
- 判断链栈是否为空
1 | bool StackEmpty(LinkStack *L) |
- 遍历
1 | void Display(LinkStack *L) |
顺序栈与链栈的对比
- 它们在时间复杂度上是一样的,均为O(1)
- 对于空间性能,顺序栈需要事先确定一个固定的长度,可能会存在内存空间浪费的问题,但它的优势是存取时定位很方便;而链栈则要求每个元素都有指针域,这同时也增加了一些内存开销,但对于栈的长度无限制
- 如果栈的使用过程中元素变化不可预料,有时很小,有时非常大,那么最好是用链栈,反之,如果它的变化在可控范围内,建议使用顺序栈会更好一些
总结
- 栈是一种特殊的线性表,可以用数组或链表来实现它,栈的入栈和出栈操作时间复杂度都是O(1)
- 两栈共享空间这样的数据结构,通常都是当两个栈的空间需求有相反关系时,也就是一个栈增长时另一个栈在缩短的情况,这样使用两栈共享空间存储方法才有比较大的意义。否则两个栈都在不停地增长,那很快就会因栈满而溢出了
- 链栈的操作绝大部分都和单链表类似,只是在插入和删除上,特殊一些
- 链栈的进展push和出栈pop操作都很简单,时间复杂度均为O(1)