之前我们学习了线性表,了解了线性表的结构与实现、应用场景,现在我们来学习栈和队列,本篇文章带你从零搞定栈。
栈的概念
栈是一种特殊的线性表,它在一端插入和删除数据,这端称为栈顶另一端就叫做栈底。栈遵循"先入后出"(也可以是后入先出)的原则。
我们可以把栈看做一个水杯,只有一端开口,里面存放的东西是线性的。

入栈:往栈顶插入数据的操作叫做入栈/压栈/进栈。
出栈:从栈顶删除元素的操作叫做出栈。

比如这张图中,我们按1 2 3 4 5入栈,出栈的顺序就变成了5 4 3 2 1,这说明了栈是先入后出的。当栈中没有数据的时候,栈顶和栈底是相等的。

我们知道线性表在逻辑结构上是线性的,物理结构上顺序表是连续的,链表不一定,那么我们采用哪种结构来实现栈呢?
栈的底层结构
如果我们用顺序表来实现栈 ,把数组开头看做栈底,数组末尾看做栈顶:

出栈和入栈都在栈顶操作,而在数组末尾插入和删除数据的时间复杂度均为O(1)。
如果我们用链表来实现栈,把头结点看成栈顶,尾结点看成栈底:

在栈顶(头结点)进行入栈和出栈操作时间复杂度也均为O(1)。
在空间不够时,顺序表要进行扩容,而链表每次插入一个新结点都要申请一个结点的空间,相比之下用顺序表来实现栈更合适些。
我们知道顺序表的结构是:
c
struct List
{
int* arr;//指向数组的指针
int size=0;//有效元素个数
int capacity;//容量大小
};
相似的,因为栈都是对栈顶进行操作,所以我们定义一个top来表示栈顶,同时也是栈中有效元素个数:

入栈的时候就往top这个位置插入数据就好了。
栈的结构如下:
c
struct Stack
{
int* arr;//指向数组的指针
int top;//栈顶
int capacity;//容量
};
然后我们就可以来实现栈了。
栈的实现
我们依然定义一个头文件Stack.h来声明栈的结构和它的接口函数(初始化、入栈、出栈等),一个源文件Stack.c来实现栈的接口函数,一个源文件main.c用来进行测试。
c
Stack.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
//栈的结构
typedef int SDataType;//数据类型重命名
typedef struct Stack
{
SDataType* arr;
int top;//栈顶
int capacity;//容量
}Stack;
//初始化
void StackInit(Stack* ps);
//入栈
void StackPush(Stack* ps, SDataType x);
//出栈
void StackPop(Stack* ps);
//销毁
void StackDestroy(Stack* ps);
//栈是否为空
bool StackEmpty(Stack* ps);
//返回栈顶元素
SDataType StackTop(Stack* ps);
//有效元素个数
int StackSize(Stack* ps);
初始化
初始化就是把arr置为NULL,top,capacity置为0就好了。

入栈
入栈就是在栈顶插入元素,在插入元素之前,我们要先判断是否要进行扩容。
当top==capacity时,说明栈空间已满或者是还未开辟空间。


第一次我们就选择开辟4个数据的空间大小,后续扩容选择扩充为原来capacity的两倍。
c
//判断是否需要扩容
if (ps->capacity == ps->top)
{
//判断是否为第一次扩容
int Newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
//扩容
SDataType* p = (SDataType*)realloc(ps->arr, Newcapacity * sizeof(SDataType));
if (p == NULL)
{
perror("realloc");
exit(-1);
}
ps->capacity = Newcapacity;
ps->arr = p;
p = NULL;
}
然后就可以进行入栈操作了,入栈操作很简单,只需要把数据插入到top位置上,top++就好了。

代码如下:

判空
判断是否为空栈,只需要判断ps->top==0这个表达式结果是否为真,表达式结果为真则返回true,否则返回false。

出栈
出栈就是在栈顶删除元素,top--就好了,就代表把最后一个元素抛弃了。

需要确保不是空栈。
销毁
销毁就是把数组arr内存释放掉,然后把arr置为NULL,top和capacity置为0就好了。

有效元素个数
top就是有效元素个数。

返回栈顶元素

需要确保不是空栈。
测试
c
#include"Stack.h"
void test()
{
Stack st;
//初始化
StackInit(&st);
//入栈
StackPush(&st, 1);
StackPush(&st, 2);
StackPush(&st, 3);
StackPush(&st, 4);
StackPush(&st, 5);
//取栈顶元素
printf("Top:%d\n", StackTop(&st));
//出栈
StackPop(&st);
StackPop(&st);
StackPop(&st);
//取栈顶元素
printf("Top:%d\n", StackTop(&st));
//有效元素个数
printf("Size:%d\n", StackSize(&st));
//销毁
StackDestroy(&st);
}
int main()
{
test();
return 0;
}
