C语言之数据结构(栈)
一、栈的定义
栈:是限定仅在表尾进行插入和删除操作的线性表。
允许插入和删除的一端为栈顶(top),另一端称为栈底(bottom),不含任何数据元素的栈称为空栈。栈又称为后进先出(Last In First Out)的线性表,简称LIFO结构。
理解栈的定义需要注意:
首先是个线性表,栈元素具有线性关系,即前驱后继关系。定义中说是在线性表的表尾进行插入和删除操作,这里的表尾是指栈顶,而不是栈底。
特殊之处:限定了这个线性表的插入和删除位置,它始终只在栈顶进行操作。这也就使得:栈底是固定的,最先进栈的只能在栈底。
栈的插入操作,叫作进栈,类似子弹入弹夹。
栈的删除操作,叫作出栈,类似弹夹中子弹出夹。
二、进栈出栈的变化形式
有个问题,这个最新先进栈的元素,是不是就只能是最后一个出栈呢?
答案:不一定,要看具体情况。栈对线性表的插入和删除的位置,进行了限制,并没有对元素的进出的时间进行限制,也就是说,在不是所有元素都进栈的情况下,事先进去的元素也可以出栈,只要保证是栈顶元素出栈就可以了。
举例说明:如果我们现在是有3个整型数据元素1、2、3依此进栈,会有哪些出栈次序呢?
第一种:1、2、3进,再3、2、1出。这是最简单的最好理解的一种,出栈次序为321.
第二种:1进,1出,2进,2出,3进,3出。也就是进一个,出一个,出栈栈次序为123.
第三种:1进,2进,2出,1出,3进,3出。出栈次序为213.
第四种:1进,1出,2进,3进,3出,2出。出栈次序132.
第五种:1进,2进,2出,3进,3出,1出。出栈次序为231.
从这个简单的例子能看出,只有3个元素,就有5种可能的出栈次序,如果元素数量多,其实出栈的变化将会更多。这个知识点一定要弄明白。
三、栈的顺序存储结构及实现
栈是线性表的特例,那么栈的顺序存储结构其实也就是线性表顺序存储的简化,简称为顺序栈。线性表是用数组来实现的,想想看,对于栈这个只能一头插入删除的线性表来说,用数组的哪一端来作为栈顶和栈底比较好。
对,没错,下标为0的一端作为栈底比较好,因为首元素都在栈底,变化小,所以让它作栈底。
我们定义一个top变量来指示栈顶元素在数组中的位置,它可以来回移动,若存储栈的长度为StackSize,则栈顶位置top必须小于StackSize。当栈存在一个元素时,top等于0,因此通常把空栈的判定条件定为top等于-1.
栈的结构定义
typedef int SElemType;SElemType 类型根据实际情况而定,这里假设为int
typedef struct
{
SElemType data[MAXSIZE]存储栈的长度
int top;
}SqStack;
若现在有一个栈,StackSize是5,则栈的普通情况,空栈和栈满的情况示意图如下:
1.进栈操作
对于栈的插入,即进栈操作
对进栈操作push,代码如下:
Status Push(SqStack *S,SElemType e)
{
if(S->top==MAXSIZE-1)栈满
{
return ERROR;
}
S->top++;栈顶指针上移
S->data[S->top]=e;将新插入元素赋值给栈顶空间
return OK;
}
2.出栈操作
代码如下(示例):
Status Pop(SqStack *S,SElemType *e)
{
if(S->top==-1)
{
return ERROR;
}
*e=S->data[S->top];将要删除的栈顶元素赋值给e指针
S->top--;指针下移
return OK;
}
三、两栈的空间空闲(顺序存储结构及实现)
使用一个数组存储两个栈
图形展示:数组有两个端点,两个栈有两个栈底,让一个栈的栈底为数组的始端,即下标为0处,另一个栈为栈的末端,即下标为数组长度n-1处,两个栈如果增加元素就是两端点向中间延续。
从上图可以分析出:
空栈:栈1的top1为-1,栈2的top2为n
栈满:
若栈2是空栈,栈1的top1等于n-1,就是栈1满了。
若栈1是空栈,栈2的top2等于0,就是栈2满了。
但多数情况是两个栈见面时,也就两个指针之间相差1时,top1+1=top2时,为栈满。
两栈共享空间的结构体代码如下:
typedef struct
{
SElemtype data[MAXSIZE];
int *top1;栈1栈顶指针
int *top2;栈2栈顶指针
}sqDoubleStack;
两栈共享空间的push方法:除了要插入元素值参数外,还需要一个判断是进入栈1 还是栈2的判断参数stackNumber,无需关系栈是否是空栈,秩序判断是否满栈即可
State Push(sqDoubuleStack *S,SElemtype e,int stackNumber)
{
if(S->top1+1==S->top2)
{
return ERROR;
}
if(stackNumber==1)
{
S->top1++;
S->data[S->top1]=e;//S->data[++S->top1]=e
}
if(stackNumber==2)
{
S->top2++;
S->data[S->top2]=e;
}
return OK;
}
两栈共享空间的pop方法:只需判断要从哪个栈出栈即可(stackNumber),无需关系栈是否满栈,只判断是否为空栈即可
State Pop(sqDoubleStack *S,SElemtype *e,int stackNumber)
{
if(stackNumber==1)
{
if(S->top1==-1)
return ERROR;
*e=S->data[S->top1--];
}
if(stackNumber==2)
{
if(S->top2==MAXSIZE)
return ERROR;
*e=S->data[S->top1++];
}
return OK;
}
总结
栈的应用:通常都是当两个栈空间需求有相反关系时,也就是说一个栈增长时另一个栈在缩短的情况下,就像买股票,你买的时候,就有人在卖。
要注意在进出栈的时候,是先移动指针,还是取出栈的值:
入栈:先移动指针,后赋值。
出栈:先取出值,后移动指针。