栈的定义
栈是限定仅在表尾进行插入和删除操作的线性表。允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom)。
*后进先出(Last In First Out)*称为LIFO结构。
顺序栈
存储结构
栈的结构定义:
typedef int SELemType;//SELemType根据实际情况而定义。
typedef struct{
SElemType data[MAXSIZE];
int top;
}SqStack;
出栈以及进栈
进栈操作push:
//插入元素e为新的栈顶元素
Status Push(SqStack *S,SELemType e){
if(S->top == MAXSIZE - 1){
return ERROR;
}
S->top++;//栈顶元素加1
S->data[S->top]=e;//将新插入的元素赋值给栈顶空间
return OK;
}
出栈操作pop:
Status Pop(SqStack *S,SELMType *e){
if(S->top == -1) return ERROR;
*e = S->data[S->top];//将删除的栈顶元素赋值给e
S->top--;//栈顶指针减1
return OK;
}
共享栈
关键思路是:top1和top2是栈1和栈2的栈顶指针,只是在数组两端,向中间靠拢。
栈1空的时候,top1为-1;栈2空的时候,top2为n。
当top1 + 1 == top2时为栈满。
两栈共享空间结构:
typedef struct{
SElemType data[MAXSIZE];
int top1;//栈1的栈顶指针
int top2;//栈2的栈顶指针
}SqDoubleStack;
push方法:
Status Push(SqDoubleStack *S,SElmType e,int stackNumber)
{
if(S->top1+1 == S->top2) return ERROR;//栈已经满了
if(stackNumber == 1) S->data[++S->top1]=e;//若栈1有元素进来,则先top1+1后给数组赋值
else if(stackNumber == 2) S->data[--S->top2]=e;//若栈2有元素进来,则先top2-1后给数组赋值
return OK
}
pop方法:
Status Pop(SqDoubleStack *S,SElmType e,int stackNumber)
{
if(stackNumber==1) {
if(S->top1=-1) return ERROR;//说明栈1位空栈
*e=S->data[S->top1--];//栈1的栈顶元素出栈
}
else if(stackNumber==2){
if(S->top2=MAXSIZE) return ERROR;//说明栈2位空栈
*e=S->data[S->top1++];//栈2的栈顶元素出栈
}
return OK
}
使用这样的数据结构,通常都是两个栈的空间需求有相反关系的时候(比如买股票)。
针对两个具有相同的数据类型的栈才适用。
链式栈
存储结构
typedef struct StackNode{
SElemType data;
srtuct StackNode *next;
}StackNode,*LinkStackPtr;
typedef struct LinkStack{
LinkStackPtr top;
int count;
}LinkStack;
出栈以及进栈
进栈push操作:
Status Push(LinkStack *S,SElemType e){
LinkStackPtr s=(LinkStackPtr)malloc(sizeof(StackNode));
s->data=e;
s->next=S->top;//把当前栈顶元素赋值给新节点的直接后继
S->top=s;//将新的结点s赋值给栈顶指针
S->count++;
return OK;
}
出栈pop操作:
Status Pop(LinkStack *S,SElemType *e){
LinkStackPtr p;
if(StackEmpty(*S)) return ERROR;
*e=S->top->data;
p=S->top;//将栈顶节点赋值给P
S->top=S->top->next;//使栈顶指针下移一位,指向后一个结点
free(p);//释放结点p
S->count--;
return OK;
}
顺序栈需要确定一个固定的长度,但是存取定位很方便。链栈长度则无限制。如果栈的使用过程中元素变化不可预料,有时很小,有时很大,或者非常大,最好使用链栈;反之则使用顺序栈。
栈的应用
递归
比如斐波那契数列,例子略。
递归:一个直接调用自己或通过一系列的调用函数间接地调用自己的函数。
四则运算表达式求值
后缀(逆波兰)表示法
一种不需要括号的后缀表达法。比如9+(3-1)*3+10/2表示为 9 3 1 - 3 * 10 2 / +.
计算规则:从左到右遍历表达式的每个数字和符号,遇到数字就进栈,遇到是符号,就将处于栈顶两个数字出栈,进行运算,运算结果进栈,一直到最终获得结果。
中缀表达式转后缀表达式
计算规则:从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分;若是符号,则判断其与栈顶符号的优先级,是右括号或者优先级不高于栈底符号(乘除优先于加减)则栈顶元素依次出栈输出,并将当前符号进栈,一直到最终输出后缀表达式为止。