参考此文章数据结构——栈,此文章写的更详细,由于我们都是学自于比特课程,这里做个自我备份,方便后续查阅、修改和补充。
前言
1.栈是什么?
栈(Stack):是只允许在一端进行插入或删除的线性表。首先栈是一种线性表,但限定这种线性表只能在某一端进行插入和删除操作。
栈顶(Top):线性表允许进行插入删除的那一端。
栈底(Bottom):固定的,不允许进行插入和删除的另一端。
空栈:不含任何元素的空表。
栈又称为后进先出(Last In First Out)的线性表,简称LIFO结构
2.栈的接口实现
栈与顺序表类似,可设计为定长的静态栈或支持动态增长的栈。然而,定长栈存在较大局限性,在实际中不太实用,因此我们主要实现支持动态增长的栈。
与之前的顺序表/链表接口实现相同,我们首先创建一个头文件“Stack.h”以及两个源文件“Stack.c”和“Test.c”,它们的具体作用如下:
- “Stack.h”:用于栈的定义、头文件的引用以及接口函数的声明。
- “Stack.c”:实现接口函数。
- “Test.c”:对各个函数进行测试。
我们先完整展示“Stack.h”的代码,同时别忘了在两个源文件中引用“Stack.h”。
#pragma once //防止头文件被二次引用
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
typedef int STDataType; //如果要修改存储的数据类型可直接在此修改
typedef struct Stack
{
STDataType* arr;
int top;
int capacity; //容量
}Stack;
void StackInit(Stack* pst);//初始化栈
void StackPush(Stack* pst, STDataType x);//入栈
void StackPop(Stack* pst);//出栈
STDataType StackTop(Stack* pst);//获取栈顶元素
int StackSize(Stack* pst);//获取栈中有效元素个数
bool StackEmpty(Stack* pst);//检测栈是否为空
void StackDestory(Stack* pst);//销毁栈
2.1初始化栈
void StackInit(Stack* pst)
{
assert(pst); //断言,防止传入空指针
pst->arr = NULL; //初始化指针,置空
pst->top = 0; //top指向栈顶数据的下一个位置
pst->capacity = 0; //初始化容量
}
类似地,我们可将结构体中的 top 近似理解为数组的下标(虽然并非完全等同,但这样理解较为方便)。当我们在初始化栈时,将 top 初始化为 0,此时栈中无数据,top 指向栈顶数据的下一个位置。
当我们将 top 初始化为 -1 时,top 会指向栈顶数据的位置。
本文我们采用的是 top 初始值为 1 的情况。
2.2入栈
void StackPush(Stack* pst, STDataType x)
{
if (pst->top == pst->capacity) //容量已满,需要扩容
{
int NewCapacity = pst->capacity == 0 ? 4 : pst->capacity * 2; //如果容量为0则扩到4,否则扩为2倍
STDataType* cmp = (STDataType*)realloc(pst->arr, NewCapacity * sizeof(STDataType));
//创建一个临时指针变量来存储新空间地址,防止开辟失败
if (cmp == NULL) //防止空间开辟失败出现空指针
{
perror("realloc fail");
return;
}
pst->arr = cmp; //将临时指针变量中存放的新空间地址赋值给arr
pst->capacity = NewCapacity; //空间容量更新
}
pst->arr[pst->top] = x; //将数据存放进栈顶元素的下一个位置
pst->top++; //位置更新
}
2.3 出栈
void StackPop(Stack* pst)
{
assert(pst); //断言,防止传入空指针
assert(!StackEmpty(pst)); //断言,用检测空栈的函数返回值来判断,栈为空则不能出栈
pst->top--; //位置更新
}
出栈只需要移动top的位置,把原来栈顶的元素“踢出”有效数据范围即可。StackEmpty函数将在后面讲到。
2.4 获取栈顶元素
STDataType StackTop(Stack* pst)
{
assert(pst); //断言,防止传入空指针
assert(!StackEmpty(pst)); //断言,用检测空栈的函数返回值来判断,栈为空则不能获取
return pst->arr[pst->top - 1]; //top-1为栈顶元素位置,返回其值即可
}
2.5 获取栈中有效元素个数
int StackSize(Stack* pst)
{
assert(pst); //断言,防止传入空指针
return pst->top; //top即为有效元素个数
}
2.6 检测栈是否为空
bool StackEmpty(Stack* pst)
{
assert(pst); //断言,防止传入空指针
return pst->top == 0; //如果top为0表达式则为真,返回值为ture,反之为false
}
2.7销毁栈
bool StackEmpty(Stack* pst)
{
assert(pst); //断言,防止传入空指针
return pst->top == 0; //如果top为0表达式则为真,返回值为ture,反之为false
}
2.8 调试代码
所有接口都完成后,我们在Test.c中调试一下
#include "Stack.h"
void TestStack()
{
ST st;
StackInit(&st);
StackPush(&st, 1);
StackPush(&st, 2);
StackPush(&st, 3);
printf("%d ", StackTop(&st));
StackPop(&st);
printf("%d ", StackTop(&st));
StackPop(&st);
StackPush(&st, 4);
StackPush(&st, 5);
while (!StackEmpty(&st))
{
printf("%d ", StackTop(&st));
StackPop(&st);
}
printf("\n");
StackDestory(&st);
}
int main()
{
TestStack();
return 0;
}