栈是一种特殊的线性数据结结构,栈只能允许对数据在固定的一端进行插入操作和删除操作。插入数据和删除数据的一端是栈顶,而另一端就是栈底。栈的性质就是数据后进先出,即LIFO原则(last in first out)。当有一列整数{1,2,3,4,5},依次让这些数入栈,出栈的顺序有多种。如1,2,3,4,5,这种情况就是进一个出一个的情况,而5,4,3,2,1就是依次入栈依次出栈的顺序。当然,2,5,4,3,1这种出栈顺序也是合法的。因为栈的出栈要讲究后进先出
栈的结构类似于一个弹夹,数据入栈操作就类似于子弹压进弹夹一样,先压的子弹就在弹夹的底部。而数据出栈操作就类似开火,后压进去的子弹在弹夹的顶部,所以,先被打出去的子弹也就是弹夹顶部的子弹。可以将栈对数据操作理解成弹弹夹对子弹的操作。
栈的实现方式分为两种,分别是数组栈实现和链式栈的实现。
从图上结构不难发现,相对链式栈而言,数组栈的插入和删除数据的代价较小,所以,通常栈的实现采用的是数组栈的结构。本篇文章也是对数组栈的方式进行讲解。
本篇文章实现的是动态增长的数组栈,相信掌握了这一种经典的结构后,其他的结构的实现对你来说也是小菜一碟。话不多说直接上菜!首先,我们需要定义一个指针来指向动态开辟内存的地址。其次就是用一个记录栈顶位置,最后就是定义一个变量来记录动态开辟内存的容量,容量如果满了,我们就对动态申请的内存进行扩容。
//重命名数据类型
//方便根据实际需求进行更改
typedef int STDataType;
typedef struct Stack
{
//通过数组形式存储数据
STDataType* data;
//记录栈顶
int top;
//数组容量
int capacity;
}ST;
//初始化栈
void StackInit(ST* ps);
//销毁栈
void StackDestroy(ST* ps);
//数据入栈
void StackPush(ST* ps, STDataType x);
//空栈判断
bool StackEmpty(ST* ps);
//数据出栈
void StackPop(ST* ps);
//栈的有效元素个数
int StackSize(ST* ps);
//获取栈顶元素
STDataType StackTop(ST* ps);
首先我们需要对形参进行判断,然后动态申请一块连续的空间存放数据。初始容量为4。记录栈顶的位置的变量初始化成0。
//初始化栈
void StackInit(ST* ps)
{
//断言判断指针有效性
assert(ps);
//初始化
//动态申请内存
STDataType* tmp = (STDataType*)malloc(sizeof(STDataType) * 4);
//判断动态内存是否申请成功
if (NULL == tmp)
{
perror("malloc fail\n");
return;
}
//初始化栈
ps->data = tmp;
ps->capacity = 4;
ps->top = 0;
}
//销毁栈
void StackDestroy(ST* ps)
{
assert(ps);
//释放动态内存
free(ps->data);
ps->data = NULL;
//置0
ps->top = ps->capacity = 0;
}
首先,判断动态申请的数组容量是否满了,满了的话就扩容。然后,插入数据。最后,移动top变量的位置,使其指向下一个数据入栈的位置。
//数据入栈
void StackPush(ST* ps, STDataType x)
{
assert(ps);
//判断数组容量,满了就扩容
if (ps->top == ps->capacity)
{
//使用临时变量保存扩容后的地址,避免异地扩容出现问题
STDataType* tmp = (STDataType*)realloc(ps->data, sizeof(STDataType) * ps->capacity * 2);
if (tmp == NULL)
{
perror("realloc fail\n");
return;
}
ps->data = tmp;
//调整数组容量
ps->capacity *= 2;
}
//压栈
ps->data[ps->top] = x;
//top指向栈顶
ps->top++;
//写成下面这种也可以
//ps->data[ps->top++] = x;
}
若控制下一个数据入栈的位置变量指向0时,表示当前栈没有数据。
//空栈判断
bool StackEmpty(ST* ps)
{
assert(ps);
//若栈顶指向0表示空栈
return ps->top == 0;
}
通过将控制数据进栈位置的变量-1,使下一次数据进栈覆盖原数据达到数据出栈的效果。需要注意当栈为空时无法出数据。
//数据出栈
void StackPop(ST* ps)
{
assert(ps);
//空栈不可删除数据
assert(!StackEmpty(ps));
//出栈
ps->top--;
}
//栈有效元素个数
int StackSize(ST* ps)
{
assert(ps);
return ps->top;
}
就是取数组中下标为有效个数-1位置处的元素。需要注意的是空栈不能取栈顶元素。
//栈顶元素
STDataType StackTop(ST* ps)
{
assert(ps);
//避免空栈越界访问
assert(!StackEmpty(ps));
return ps->data[ps->top - 1];
}