定义:栈是限定仅在表尾进行插入和删除操作的线性表。
把允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom),不含任何数据元素的栈称为空栈。栈又称为后进先出的线性表,简称为LIFO。
栈的插入操作,叫做进栈,也称为压栈或者入栈。
栈的删除操作,叫做出栈,也称为弹栈。
栈的顺序存储结构
因为栈是线性表的一种特殊形式,所以用数组实现栈的顺序存储结构。
我们约定,下标为0的一端作为栈底。定义一个top变量来指示栈顶元素在数组中的位置。约定把空栈的判定条件定为top等于-1。
栈的结构定义:
#define MAXSIZE 100;
typedef int SElemType;
typedef struct
{
SElemType data[MAXSIZE];
int top; /*用于栈顶指针*/
}SqStack;
栈的顺序存储结构-----进栈
C语言代码如下:
/*插入元素e为新的栈顶元素*/
int Push(SqStack *S, SElemType e)
{
if (S->top == MAXSIZE - 1)
{
return 0;
}
S->top++;
S->data[S->top] = e;
return 1;
}
栈的顺序存储结构---出栈
C语言代码如下:
/*若栈不空,则删除S的栈顶元素,用e返回其值,并返回1;否则返回0*/
int Pop(SqStack *L, SElemType *e)
{
if (S->top == -1)
{
return 0;
}
*e = S->data[S->top];
return 1;
}
说明:栈的顺序存储结构的进栈和出栈都没有涉及循环,时间复杂度都为O(1)。
两栈共享空间
如果有两个相同类型的栈,我们为他们各自开辟了数组空间,极有可能是第一个栈满了 ,再进栈就溢出了,而另一个栈还有很多存储空间空闲。因此我们完全可用一个数组来存储两个栈。
做法:数组有两个端点,两个栈有两个栈底,让一个栈的栈底为数组的始端,即下标为0处,另一个栈的栈底为栈的末端,即下标为n-1处。这样,两个栈如果增加元素,就是两端点向中间延伸。
我们约定top1和top2是两个栈的栈顶指针。
栈1为空时,top1=-1;栈2空时,top2=n。
当栈满时有top2 = top1+1。
两栈共享空间结构代码如下:
/*两栈共享空间结构*/
typedef struct
{
SElemType data[MAXSIZE];
int top1;
int top2;
}SqDoubleStack;
两栈共享空间的进栈操作
/*插入元素e为新的栈顶元素*/
int Push(SqDoubleStack *S, SElemType e, int stackNumber)
{
if (S->top1+1==S->top2) /*栈已经满,不能再push新元素*/
{
return 0;
}
if (stackNumber == 1) /*栈1有元素进栈*/
{
S->data[++S->top1] = e /*若栈1则先top1+1后给数组元素赋值*/
}
else if(stackNumber == 2)
{
S->data[--S->top2] = e;
}
return 1;
}
两栈共享空间出栈操作
/*若栈不空,则删除S的栈顶元素,用e返回其值,并返回1,否则返回0*/
int Pop(SqDoubleStack *S, SElemType *e, int stackNumber)
{
if (stackNumber == 1)
{
if (S->top1 == -1)
{
return 0;
}
*e = S->data[S->top1--];
}
else if (stackNumber == 2)
{
if (S->top2 == MAXSIZE)
{
return 0;
}
*e = S->data[S->top2++];
}
return 1;
}
说明:使用两栈共享空间这样的数据结构,通常都是当两个栈的空间需求有相反关系时,也就是一个栈增长时另一个栈在缩短的情况。当然,这只是针对两个具有相同数据类型的栈的一个设计上的技巧。
栈的链式存储结构----简称链栈
由于单链表有头指针,而栈顶指针也是必须的,因此比较好的方法时把栈顶放在单链表的头部。通常对于链栈来说,是不需要头结点的。
链栈的结构定义:
typedef int SElemType;
typedef struct StackNode
{
SElemType data;
struct StackNode *next;
}StackNode;
typedef StackNode * LinkStackPtr;
typedef struct LinkStack
{
LinkStackPtr top; //栈顶指针
int count;
}LinkStack;
链栈的进栈操作:
/*插入元素e为新的栈顶元素*/
int Push(LinkStack *S, SElemType e)
{
LinkStackPtr s = (LinkStackPtr)malloc(sizeof(StackNode));
s->data = e;
s->next = S->top;//把当前的栈顶元素赋值给新结点的直接后继
S->top = s;
S->count++;
return 1;
}
链栈的出栈操作:
/*若栈不为空,则删除S的栈顶元素,用e返回其值,并返回1;否则返回0*/
int Pop(LinkStack *S, SElemType *e)
{
LinkStackPtr p;
if (S->top == NULL)
{
return 0;
}
*e = S->top->data;
p = S->top;
S->top = S->top->next;
free(p);
S->count--;
return 1;
}
说明:对于链栈的进栈和出栈操作,它们的时间复杂度都为O(1)。