栈:限定仅在表尾进行插入和删除操作的线性表。
栈顶:允许删除和插入的一端,即我们提到的表尾。
LIFO结构:后进先出的线性表。
我们从进出栈的一个简单问题来深入了解栈的含义以及栈的进出规则。
问题描述一:
假设有1、2、3三个数据依次进栈,有哪些出栈的可能性?是否只可能是3、2、1出来的情况。
分析:问题我们只要求按照1、2、3的顺序进栈,但是否一定要把所有的数据进栈以后才能够出来呢?答案是否定的。
我们可以(1)1进1出,2、3进,3、2出。则有1、3、2的顺序
(2)1进1出,2进2出,3进3出,则有1、2、3的顺序
(3)1进、2进,2出、1出,则有2、1、3的顺序
(4)1进、2进2出、3进,则有2、3、1的顺序。
那是否有312的顺序呢?答案是否定的,因为3出,则12必然先进去了,且12不出来,那结果只可能是321。
此处给出了3个元素的进出栈,总共有5种出栈的情况。对于随着元素的增加,那出栈的情况与可能性会更多一点。
栈的基本概念:
1:抽象数据类型
理论上说线性表具备的操作类型它都具备。由于其特殊性,所以针对它在操作上会有些变化。特别是插入和删除操作。我们改名为push和pop。
Push(*S,*e);
Pop(*S,*e);
2:栈的顺序存储结构和实现
对于栈的顺序存储结构,首先是确定哪一端是栈顶,哪一端是栈底。我们一般是选用下标是0的一端作为栈底。
注意:用一个top变量来指示栈顶元素在数组中的位置。当栈中存在一个元素时,top等于0,因此通常把空栈的判定条件定位top为-1。
(1)栈的结构:
typedef int SElemType;
typedef struct
{
SElemType data[MAXSIZE];
int top;
}SqStack;
(2)进栈与出栈结构:
进栈:
Status Push(SqStack *S,SElemType e)
{
if(S->top==MAXSIZE-1)
{
return error;
}
S->top++;
S->data[S->top]=e;
return OK;
}
出栈:
Status Pop(SqStack *S,SEleType *e)
{
if(S->top==-1)
{
return error;
}
*e=S->data[S->top];
S->top--;
return OK;
}
两栈共享空间的结构:
typedef struct
{
SElemType data[MAXSIZE];
int top1;
int top2;
}SqDoubleStack;
对于两栈共享空间的push方法,除了要插入元素值参数外,还需要有一个判断栈1和栈2的栈号参数stackNumber。
两栈共享空间的插入与删除元素。
两栈共享空间插入元素:
Satus Push(SqDoubleStack *S,SElem
Type e,int stackNubmber)
{
if(S->top1+1==S->top2)
return error;
if(stackNumber==1)
S->data[++S->top1]=e;
else if(stackNubmer==2)
S->data[--S->top2]=e;
return Ok;
}
两栈共享空间删除元素:
Status Pop(SqDoubleStack *S,SElemType *e,int stackNumber)
{
if(stackNumber==1)
{
if(S->top1==-1)
return error;
*e=S->data[S->top--];
}
else if(stackNumber==2)
{
if(S->top2==MAXSIZE)
return error;
*e=S->data[S->top2++];
}
return OK;
}
3:栈的链式存储结构
链栈的结构代码:
typedef struct StackNode
{
SElemType data;
struct 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->count++;
return OK;
}
pop操作
Status Pop(LinkStack *S,SElemType *e)
{
LinkStackPtr p;
if(StackEmpty(*S))
return error;
*e=S->top->data;
p=S->top;
S->top=S->top->next;
free(p);
S->count--;
return OK;
}
上面是对栈的定义及其基本的操作的一个回顾,关于栈的应用,有一个最主要的运用就是四则运算表达式求值。
运用栈结构进行四则运算表达式的求值:
基本原理:后缀表达式
特点:后缀表达式所有的符号都在数字后面,以”9+(3-1)*3+10÷2“为例,后缀表达式就是:”9 3 1 -3*+10 2 / +“;
如何由中缀表达式转换成后缀表达式?
9+(3-1)*3+10÷2叫做中缀表达式
规则:从左到右,遍历整个表达式,遇到数字就输出;
若是符号就判断其与栈顶符号的优先级,是右括号或优先级低于栈顶的符号(乘除优先于加减),则栈顶元素依次出栈并输出,并将当前符号进栈,一直到最终输出后缀表达式为止。
分析:(1)数字9输出;
(2)符号”+“入栈,数字3输出,符号”(“进栈,符号”-“入栈,接着是符号”)“,遇到右括号,则栈顶元素依次出栈,直”(“出栈为止。所以此时结果931-
(3)紧接着是符号”*“,因为此时的栈顶符号位”+“号,优先级别低于”*“,所以不输出,”*“进栈
(4)符号”+“,此时当前栈顶元素”*“,低于栈顶,因此栈顶的元素依次输出,并把当前的符号”+“进栈,于是结果是9 3 1 -3*+
(5)接着是输出数字10,后面的符号是”÷“,所以”/“进栈,最后输出2,总的表达式是9 3 1- 3* +10 2,;
(6)最后输出表达式/ +;所以有后缀表达式为9 3 1 - 3* + 10 2 / +
如何用后缀表达式计算结果:
规则:从左到右遍历表达式的每个数字和符号。遇到数字就进栈,遇到符号就将处于栈顶两个数字出栈,进行运算,结果再进栈,最后获得自己想要的结果。
问题描述:
输入一个只包含个位数字的简单四则运算表达式字符串,计算该表达式的值;
注:1:只包含+、-、*、/四则运算符,不含括号
2:表示式数值只包含个位整数,且不会出现0作为除数的情况
3:要考虑+、-、*、/的计算优先级别
4:除法用整数除法,即仅保留运算结果的整数部分
5:输入字符串一定是符合题意的合法的表达式
要求实现函数:int calculate(int len,char *expStr)
示例:1)char *expStr=”1+4*5-8/3“;返回结果是:19
2)char *expStr="8/3*3';返回结果是:6
分析:这就是一个典型的运用栈实现四则运算的题目,考虑的思路,首先将中缀表达式转换成后缀表达式,然后进行运算。
#include <string.h>
#include <stdio.h>
#include "assert.h"
#include "string"
struct stack{//存放后缀表达式,模拟栈
char str[80];
int top;
};
struct sstack{
int str[80];
int top;
};
int calculate(int len,char *expStr)
{
char *postexp=new char[len+1];
stack opstack;
sstack calstack;
calstack.top=-1;
opstack.top=-1;
int i=0;
int k=0;
/****将中缀表达式转换成后缀表达式***/
while(expStr[i]!='\0')
{
if(expStr[i]>='0'&&expStr[i]<=9)//在中缀表达式中,如果是数字,则先进入待完成的后缀表达式的数组中
{
postexp[k++]=expStr[i];
}
else if(expStr[i]=='+'||expStr[i]=='-')//在中缀表达式中,如果是"+ or -",用另一个栈来存储
{
while(opstack.top>=0)
{
postexp[k++]=opstack.str[opstack.top--];
}
opstack.top++;
opstack.str[opstack.top]=expStr[i];
}
else if(expStr[i]=='*'||expStr[i]=='/')//在中缀表达死中,如果是"* /",先判断栈顶的符号是什么
{
while(opstack.top>=0&&(opstack.str[opstack.top]=='*'||opstack.str[opstack.top]=='/'))
{
postexp[k++]=opstack.str[opstack.top--];
}
opstack.top++;
opstack.str[opstack.top]=expStr[i];
}
i++;
}
while (opstack.top>=0)
{
postexp[k++]=opstack.str[opstack.top--];
}
/****运用中缀表达式进行四则运算****/
int temp1=0;
int temp2=0;
for(i=0;i<len;i++)
{
if(postexp[i]>='0'&&postexp[i]<='9')
{
calstack.top++;
calstack.str[calstack.top]=postexp[i]-'0';
}
else if(postexp[i]=='-')
{
temp1=calstack.str[calstack.top--];
temp2=calstack.str[calstack.top];
calstack.str[calstack.top]=temp2-temp1;
}
else if(postexp[i]=='+')
{
temp1=calstack.str[calstack.top--];
temp2=calstack.str[calstack.top];
calstack.str[calstack.top]=temp2+temp1;
}
else if(postexp[i]=='*')
{
temp1=calstack.str[calstack.top--];
temp2=calstack.str[calstack.top];
calstack.str[calstack.top]=temp2*temp1;
}
else if(postexp[i]=='/')
{
temp1=calstack.str[calstack.top--];
temp2=calstack.str[calstack.top];
calstack.str[calstack.top]=temp2/temp1;
}
}
printf("%d\n",calstack.str[calstack.top]);
return calstack.str[calstack.top];
}
int main()
{
char *expStr = "1+4*5-8/3";
int len=strlen(expStr);
calculate(len,expStr);
}