一、题目
用栈实现运算表达式的求解。方法:通常称为“算符优先法”。
输入:(5+3*(4+1))/5# //注:默认输入正确,多个回车都不行,回车也是字符,括号用英文输入
输出:4
二、题目分析
任何一个表达式,都有操作数,运算符,界限符组成。操作数一般为常量。运算符可以分为算术运算符,关系运算符和逻辑运算符。基本界限符有左右括号和表达式结束符组成。这里为了简单起见,输入的表达式仅含有加减乘除。
算符的优先法,是根据先乘除,后加减;从左到右;先括号内,后括号外。
为输入方便,表达式以#结束,与之对称的是程序自己预先在表达式的左边加上#。
为了实现算符优先算法,可以使用两个工作栈。一个称作optr,用来寄存运算符(界限符也存入其中);另一个称作opnd,用来寄存操作数。
算法的基本思想:
1、首先置操作数为空栈,表达式以#为运算符栈的栈低元素;
2、依次读入表达式中的每个字符,若为操作数,则进opnd栈,否则为运算符(包含界限符,全文皆是)。运算符要先
先进行优先权的比较,然后进行操作。
那么问题来了,优先关系是什么,否则无法比较优先权
算符间的优先关系
+ |
- |
* |
/ |
( |
) |
# |
|||||||
+ | > |
> |
< |
< |
< |
> |
> |
||||||
- | > |
> |
< |
< |
< |
> |
> |
||||||
* | > |
> |
> |
> |
< |
> |
> |
||||||
/ | > |
> |
> |
> |
< |
> |
> |
||||||
( | < |
< |
< |
< |
< |
= |
|||||||
) | > |
> |
> |
> |
> |
> |
|||||||
# | < |
< |
< |
< |
< |
= |
三、详细的思路过程
evaluateexpression()
{
initstack(optr);push(optr,'#');
initstackstack(opnd);
c=getchar();
while(c!='#'||gettop(optr)!='#')
{
if(!in(c,op))
{
push(opnd,c);
c=getchar();
}
else
{
switch(precede(gettop(optr),c))
{
case '<':push(optr,c); c=getchar;break;
case'=':pop(optr,x);c=getchar();break;
case'>':pop(optr,theat);pop(opnd,b);pop(opnd,a);
push(opnd,operate(a,theat,b));break;
}
}
}
returngettop(opnd);
}
**********************************************************************************************************************
四、代码
#include
#include
#define OPTR_INIT_STACK_SIZE 10//operator:运算符
#define OPND_INIT_STACK_SIZE 20//operand:操作数
#define INCREASEMENT 2//每次增加两个空间
char op_pri[7][7]=//每行或每列分别+-*/()#,当Θ1>Θ2,执行 //Priority:优先
{ //在某种程度上,#为了清栈,是运算符优先级最低的符号
// + - * / ( ) #
{'>','>','<','<','<','>','>'},
{'>','>','<','<','<','>','>'},
{'>','>','>','>','<','>','>'},
{'>','>','>','>','<','>','>'},
{'<','<','<','<','<','=','0'},
{'>','>','>','>','0','>','>'},//0就是 个数,默认表达式的输入不会出现语法错误
{'<','<','<','<','<','0','='},
};
typedef struct
{
char *base;
char *top;
int stacksize;
}optr_sqstack;
typedef struct
{
int *base;
int *top;
int stacksize;
}opnd_sqstack;
void init_optr_stack(optr_sqstack *s)
{
s->base=(char *)malloc(OPTR_INIT_STACK_SIZE*sizeof(char));
s->top=s->base;
s->stacksize=OPTR_INIT_STACK_SIZE;
}
void init_opnd_stack(opnd_sqstack *s)
{
s->base=(int *)malloc(OPTR_INIT_STACK_SIZE*sizeof(int));
s->top=s->base;
s->stacksize=OPND_INIT_STACK_SIZE;
}
void push_optr(optr_sqstack *s,char e)
{
if(s->top-s->base>s->stacksize)
{
s->base=(char *)realloc(s->base,(s->stacksize+INCREASEMENT)*sizeof(char));
s->top=s->base+s->stacksize;
s->stacksize+=INCREASEMENT;
}
*s->top++=e;printf("@***");
}
void push_opnd(opnd_sqstack *s,int e)
{
if(s->top-s->base>s->stacksize)
{
s->base=(int *)realloc(s->base,(s->stacksize+INCREASEMENT)*sizeof(int));
s->top=s->base+s->stacksize;
s->stacksize+=INCREASEMENT;
}
*s->top++=e;printf("$***");
}
int switch_optr(char a)
{
int temp=0;
switch(a)
{
case '+':temp=0;break;
case '-':temp=1;break;
case '*':temp=2;break;
case '/':temp=3;break;
case '(':temp=4;break;
case ')':temp=5;break;
case '#':temp=6;break;
}
return temp;
}
char priority(char a,char b)
{
int i,j;
i=switch_optr(a);
j=switch_optr(b);
return op_pri[i][j];
}
void evaluateexpression(optr_sqstack *optr,opnd_sqstack *opnd)
{
int a,b,c;
char ch,fore,temp_pro;
char chuzhanfuhao;//一般人看不懂的符号
ch=getchar();
while(!(*--optr->top=='#'&&ch=='#'))//最后的#是肯定不会放入栈中的,因为它的优先级最低
{ //运算完成的标志是:栈中只有一个#,栈外有个#号,此时必须结束
//否则,当#放入时,取=条件,程序还会接着运行
optr->top++;//归位 //ch=='#'&&*--optr->top=='#',这样写不行,因为判断前面为假,就不进行后面的判断了
if(!(34top;
optr->top++;
temp_pro=priority(fore,ch);
switch(temp_pro)
{
case '<':push_optr(optr,ch);ch=getchar();break;
case '=':optr->top--;ch=getchar();break;
case '>':a=*--opnd->top;
b=*--opnd->top;
chuzhanfuhao=*--optr->top;
switch(chuzhanfuhao)
{
case '+':c=b+a;break;
case '-':c=b-a;break;
case '*':c=b*a;break;
case '/':c=b/a;break; //c=a chuzhanfuhao b;
}
push_opnd(opnd,c);
break; //由于这次的符号仍需要和栈中下一个符号进行比较
} //故,不进栈,不需要再次getchar(),从而导致每次
} //上面的如果进行选择,则需输入下一个字符
}
}
void main()
{
int a;
optr_sqstack optr;
opnd_sqstack opnd;
init_optr_stack(&optr);
push_optr(&optr,'#');
init_opnd_stack(&opnd);
printf("输入可以进行整除的四则运算表达式,以#结束:");
evaluateexpression(&optr,&opnd);
a=*--opnd.top;
printf("运算结果为:%d",a);
}
五、代码分析
1、要理解建立符号优先权表。
2、运算完成的标志是:栈中只有一个#,栈外有个#号;
3、&&运算,a&&b,如果a为假,则不再进行b的判断。想以下这种情况则会受到影响
if( !(*--optr->top=='#'&&ch=='#')) optr->top++;
if( !(ch=='#'&&*--optr->top=='#')) optr->top++;
4、a=*--opnd->top; b=*--opnd->top;注意运算时是 b 符号 a.
六、思考
如果栈中没有提前放入一个#,会是什么情况?
我的想法:#的优先级最低,这样其与符号相比,都为<,即当前的运算符的优先级比上一个大,不执行。但最后一个一直进不了栈的
#的优先级也比所有 的运算符的优先级低,所以一直消耗栈中的符号,从而达到清理栈的作用。
上面是#的作用。
此时如果开头没有#,则第一个入栈的符号无法比较,需要进行下单独处理,把它放在循环的外面。从第二次起,进行
上述循环就成了。但多个#,明显简单些。