目录
一、前言
二、什么是栈?
三、栈各个接口的实现
1、栈元素的结构定义
2、栈的初始化
2、栈元素的插入(入栈)
3、出栈
4、栈的销毁
5、返回栈顶元素
6、返回栈元素个数
7、判断栈是否为空
三、结言
本篇文章涉及顺序表的相关知识,如有不知何为顺序表的童鞋,请自行翻阅上篇博客---线性表之顺序表。
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO (Last ln First Qut)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。
由下图理解什么叫做后进先出原则。
为了方便大家理解和测试代码,在这里我先给出各个接口的测试代码。
void test1()
{
ST p;
StackInit(&p);
StackPush(&p, 1);
StackPush(&p, 2);
printf("%d ", StackTop(&p));
StackPop(&p);
StackPush(&p, 3);
StackPush(&p, 4);
while(!StackEmpty(&p))
{
printf("%d ", StackTop(&p));
StackPop(&p);
}
StackDestroy(&p);
}
int main()
{
test1();
return 0;
}
typedef int SDataType;
typedef struct stack
{
SDataType* a; //这里的栈是使用动态开辟的数组
int top; //代表栈顶
int capacity; //栈的容量
}ST;
初始化栈较简单,我们只需要让指针a指向NULL,top和capacity等于0。
void StackInit(ST* ps)
{
assert(ps);
ps->a = NULL;
ps->top = 0;
ps->capacity = 0;
}
我们让数组的首部作为栈顶,尾部作为栈顶,栈是后进先出的,所以我们插入元素时(即入栈),只需要在数组的尾部插入;删除元素时(即出栈),只需要让top-1;这样做就满足了栈的定义。
void StackPush(ST* ps, SDataType x)
{
assert(ps);
if (ps->top == ps->capacity)
{
int NewCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SDataType* temp = realloc(ps->a, sizeof(SDataType) * NewCapacity);
if (temp == NULL)
{
printf("realloc fail.\n");
exit(-1);
}
ps->a = temp;
ps->capacity = NewCapacity;
}
ps->a[ps->top] = x;
ps->top++;
}
当top==capacity时,说明需要扩容,扩容的两种情况,当capacity为0时,什么栈一个元素也没有,这个时候我们就分配4个空间给指针a,如果capacity!=0,我们就让capacity*2,让指针a指向的空间扩大2倍。
出栈时,我们要保证有元素可以出栈,因此我们使用了assert(ps->top>0)。
void StackPop(ST* ps)
{
assert(ps);
assert(ps->top > 0);
ps->top--;
}
我们需要让指针a的空间释放,然后将top和capacity赋值为0。
void StackDestroy(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->top = 0;//没有写
ps->capacity =0;//没有写
}
栈顶元素的下标为top-1,因此我们只需要返回该下标的元素就行;同时我们要保证栈是有元素的。
SDataType StackTop(ST* ps)
{
assert(ps);
assert(ps->top > 0);
return (ps->a[ps->top - 1]);
}
top的数值为几,则栈就有多少个元素,因此我们只需要返回top的数值。
int StackSize(ST* ps)
{
assert(ps);
return ps->top;
}
我们只需要判断top是否等于0。
bool StackEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
本篇博客中,我们用的是数组完成栈的操作,是顺序表的延伸使用,所以大家主要理解顺序表是如何实现的,顺序表具体实现请看我的上篇博客。当然,我们也可以使用链表来完成栈的操作,但是链表栈对于数组栈是没有优势的(具体原因我会在以后的关于链表的博客说明),因此我们通常使用数组来完成栈的操作。