本篇目标:
1.了解栈的思想
2.掌握栈的增删操作
3.学会运用栈到算法题中
一.初步认识栈
1.基本概念
线性表 :是由相同类型的n(n>=0)个数据元素组成的有限序列,若用L命名为线性表,则⼀般表
示:为L=(a1 ,a2 ,...,aN).。
那么什么是栈呢?
栈(stack)是限定仅在⼀端进行插入或删除操作的线性表 。因此,对栈来说进行插⼊删除数据的这⼀
端称为栈顶(top),另⼀端称为栈底(bottom) ,不含元素的空表称为空栈 。栈中的数据元素遵循后
进先出(LIFOLastInFirstOut)的特性。
注意:栈插入和删除数据都在栈顶端,插入数据⼀般叫做入栈/进栈,删除数据⼀般叫做出栈。
我们也可以通过下面的示意图来理解:

现实中⼿枪弹夹和⽻⽑球桶就是⼀个栈结构,如图:

2.入栈出栈顺序
假设入栈顺序为a1,a2,a3,a4,a5,那么可能的出栈顺序有哪些呢?
最常见的思路是所有元素先入栈再依次出栈,即a5,a4,a3,a2,a1。但实际上,出栈顺序可以有很多变化。例如:
- 每个元素入栈后立即出栈,顺序就是a1,a2,a3,a4,a5
- 先入栈a1,a2,a3,然后依次出栈a3,a2,接着入栈a4,a5并出栈a5,a4,最后出栈a1,形成a3,a2,a5,a4,a1
通过不同的入栈和出栈时机组合,可以产生多种合法的出栈顺序。
示意图:

3.栈的存储
3.1.栈的分类
栈是一种特殊线性表,仅允许在一端进行元素的插入和删除操作。由于线性表具有顺序和链式两种
存储结构,栈同样可以采用这两种实现方式。需要注意的是,若使用单链表实现栈,入栈时需要遍
历到尾节点,效率较低。因此,采用双链表实现栈会比单链表更高效。本文主要介绍顺序存储的实
现方式,关于链式存储的内容将在后续补充。
可以通过如图了解:

3.2.栈的结构
跟顺序表类似,栈的顺序存储实现可以使用静态数组,我们称为静态顺序栈 ,也可以使用堆上动态
申请数组实现 ,我们称为动态顺序栈。静态顺序栈的缺陷跟静态顺序表类似,只适用于确定知道最
多需要多少空间的场景,那么我们后续重点讲解动态数组实现的版本。
静态顺序栈的结构:
cpp
#define MAX_SIZE 10
typedef int STDataType;
typedef struct Stack
{
STDataType arr[MAX_SIZE]; // 指向栈数组空间的指针
int top; // 栈顶位置
}Stack;
动态顺序栈的结构:
cpp
typedef int STDataType;
typedef struct Stack
{
STDataType* arr; // 指向栈数组空间的指针
int top; // 栈顶位置
int capacity; // 容量
}Stack;
3.3.易错点
在实现栈结构时,首先需要明确的是初始化时top的取值问题:应该设为0还是-1?
两种初始化方式的区别在于:
- 当
top=-1时,直接指向栈顶元素。此时入栈操作需要先执行++top,再将数据存入top位置。 - 当
top=0时,指向栈顶元素的下一个空位。此时入栈操作需要先将数据存入top位置,再执行++top。
需要注意的是,其他接口函数(如判空、获取栈顶元素等)的实现细节也需要与选择的初始化方式保持一致。这两种方式在实际使用中各有特点,选择哪种完全取决于个人习惯,没有绝对的优劣之分。
为何初始化时top=0,不能代表top指向栈顶元素呢?那我们要思考栈为空和栈只有⼀个数据时,
top都是0,如何区分呢?
通过图片来理解:

本次我采用的是top为0。
二.代码实现
1.主要实现
在Stack.h中,
cpp
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef int STDataType;
typedef struct Stack
{
STDataType* arr; // 指向栈数组空间的指针
int top; // 栈顶位置
int capacity; // 容量
}Stack;
// 栈的初始化
void StackInit(Stack* st);
// 栈的销毁
void StackDestroy(Stack* st);
// x元素⼊栈(进栈)
void StackPush(Stack* st, STDataType x);
// 将栈顶元素出栈,并⽤返回栈顶元素
STDataType StackPop(Stack* st);
// 获取栈顶元素并返回
STDataType StackTop(Stack* st);
// 获取栈中有效元素个数
int StackSize(Stack* st);
// 检测栈是否为空,如果是空返回真,否则返回假
bool StackEmpty(Stack* st);
2.接口实现
在Stack.c中,
2.1.初始化
cpp
void StackInit(Stack* st)
{
assert(st);
st->arr = (STDataType*)malloc(sizeof(STDataType) * 4);
if (st->arr == NULL)
{
perror("申请空间失败\n");
exit(1);
}
st->top = 0;
st->capacity = 4;
}
2.2.入栈
cpp
// x元素入栈(进栈)
void StackPush(Stack* st, STDataType x)
{
assert(st&&st->arr);
//检查一下是否要扩容?
if (st->top == st->capacity)
{
STDataType* tmp = (STDataType*)realloc(st->arr,sizeof(STDataType) * st->capacity * 2);
if (tmp == NULL)
{
perror("扩容失败\n");
exit(1);
}
st->arr = tmp;
st->capacity *= 2;
}
st->arr[st->top++] = x;
}
入栈时,记得要检查一下栈中的容量是不是不够了。
示意图:

2.3.出栈
cpp
// 将栈顶元素出栈,并⽤返回栈顶元素
STDataType StackPop(Stack* st)
{
assert(st&&st->arr);
//要检查栈不为空的情况
assert(st->top!=0);
STDataType x = st->arr[st->top-1];
st->top--;
return x;
}
出栈之前要先检查栈是否为空,否则就会导致top减为负数了。
示意图:

2.4.取栈顶元素
cpp
// 将栈顶元素出栈,并⽤返回栈顶元素
STDataType StackPop(Stack* st)
{
assert(st&&st->arr);
//要检查栈不为空的情况
assert(st->top!=0);
STDataType x = st->arr[st->top-1];
st->top--;
return x;
}
2.5.销毁
cpp
// 栈的销毁
void StackDestroy(Stack* st)
{
assert(st);
if (st->arr)
{
st->top = 0;
st->capacity = 0;
free(st->arr);
st->arr = NULL;
}
}
2.6.其他接口
cpp
// 检测栈是否为空,如果是空返回真,否则返回假
bool StackEmpty(Stack* st)
{
assert(st);
return st->top == 0;
}
cpp
// 获取栈中有效元素个数
int StackSize(Stack* st)
{
assert(st);
return st->top;
}
3.测试代码
cpp
#include "Stack.h"
int main()
{
Stack st;
StackInit(&st);
//入栈,检测栈顶数据元素和栈中有效数据元素是否正确
StackPush(&st, 1);
StackPush(&st, 2);
StackPush(&st, 3);
StackPush(&st, 4);
StackPush(&st, 5);
printf("栈中元素总数为: %d\n", StackSize(&st));
printf("栈顶元素为: %d\n", StackTop(&st));
// 出栈
printf("出栈:栈顶元素为: %d\n", StackPop(&st));
printf("出栈:栈顶元素为: %d\n", StackPop(&st));
printf("出栈:栈顶元素为: %d\n", StackPop(&st));
printf("出栈:栈顶元素为: %d\n", StackPop(&st));
printf("出栈:栈顶元素为: %d\n", StackPop(&st));
// 断言报错
// printf("出栈:栈顶元素为: %d\n", StackPop(&s));
// printf("栈顶元素为: %d\n", StackTop(&s));
StackPush(&st, 6);
StackPush(&st, 7);
// 出栈
printf("出栈:栈顶元素为: %d\n", StackPop(&st));
printf("出栈:栈顶元素为: %d\n", StackPop(&st));
StackDestroy(&st);
return 0;
}