引言:
最近学习数据结构的栈,栈是一个思想相当有意思,而且结构很独特的数据结构。栈分为两种,一种是顺序栈,栈的每个存储数据在物理上都彼此相连,另一种是链式栈,每个数据逻辑上是彼此相连的,但是在计算机内部存储不一定相连。下面将对顺序栈介绍。
顺序栈的思想:
顺序栈本质上,是一个特殊的顺序表,它只能在表末端进行删除和插入操作。类比一下,就是一个只能在末端进行插入和删除的数组。因此它有:先进后出,后进先出的特性。
所有数据,最先进入顺序栈的被放到栈的底部,称为栈底,也当然在最后才被拿出来。最后进入顺序栈的,会被放在栈顶上,也是最先被拿出来的。为了更加形象地理解,可以把栈理解成一个口袋,最先放进去的东西放在了最下面,被最后拿出来,最后才放进去的东西,放在了口袋上面,被最先拿出来。
把东西放进栈中称为入栈,把东西拿出栈,被称为出栈。
栈的实现:
栈,有三个指标,第一个是栈存放的位置,为了方便,同时担当栈底位置,这里用一个指针实现,称为栈底指针;第二个是栈顶的位置,这里也用一个指针实现,称为栈顶指针,通常,栈顶指针指的位置并不存放数据,栈顶指针位于最后一个栈元素的后一个单元;第三个是栈的大小,用一个变量来记录栈能存放多大的数据。
于是有了栈的模板(这里指针的类型由栈中数据类型决定):
typedef struct{
int *base;
int *top;
int stacksize;
}SqStack;
如何表示一个空栈?
从上面的形象图知道,当我们的两个指针重叠,即top==bottom时,这样,就可以表示一个空栈。
如何表示一个满栈?
我们知道,当两个指针重叠时,即栈顶指针top-栈底指针bottom==0时,栈为空。我们发现,栈顶指针与栈底指针的差值,就是栈现在元素的个数。因此,top-bottom==stacksize(stacksize是栈最大容量)时,栈满。
栈的基础操作:
初始化一个栈。
查看栈顶元素。
出栈——删除栈中一个元素。
入栈——给栈添加一个元素。
总代码实现:
#include<stdio.h>
#include<stdlib.h>
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
typedef struct{
int *base;
int *top;
int stacksize;
}SqStack;
SqStack S;
//TODO 定义一个栈
void Init_Stack(SqStack *S); //TODO 初始化一个栈
int Pop_Stack(SqStack *S); //TODO 删除一个元素
int GetTop_Stack(SqStack *S); //TODO 得到栈顶元素
void Push_Stack(SqStack *S, int e); //TODO 插入一个元素
void Init_Stack(SqStack *S)
//* 分为分配地址和空间,设置顶指针,设置大小
{
S->base = (int *)malloc(STACK_INIT_SIZE * sizeof(SqStack));
if (!S->base)
{
return;
}
S->top = S->base;
//* 当两个指针重合时,表示空栈
S->stacksize = STACK_INIT_SIZE;
//* 栈最初设置为标准大小
}
int GetTop_Stack(SqStack *S)
//* 定义返回值e, 确定返回值e,返回e
{
int e = 0;
if (S->top == S->base)
{
return;
}
e = *(S->top - 1);
//* 返回栈顶值
return e;
}
void Push_Stack(SqStack *S, int e)
//* 两种情况考虑,满栈与非满栈
{
if (S->top - S->base >= S->stacksize)
//* 分为,重新分配地址和空间,更新栈顶指针top,扩大容量
{
S->base = (int *)realloc(S->base, (S->stacksize + STACKINCREMENT) * sizeof(int));
if (!S->base)
{
return;
}
S->top = S->base + S->stacksize;
S->stacksize += STACKINCREMENT;
}
*S->top++ = e;
//* 插入值,然后更新栈顶指针
return;
}
int Pop_Stack(SqStack *S)
//* 设定返回值e,把顶部数据交给e,然后下移栈顶指针,返回e
{
if (S->top == S->base)
{
return;
}
int e = *--S->top;
return e;
}