栈实现运算表达式的求解

一、题目

 用栈实现运算表达式的求解。方法:通常称为“算符优先法”。

 输入:(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<stdio.h>
#include<stdlib.h>
#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(!(34<ch&&ch<48))//运算符的ASIIC的范围是40~47,#为35 
		{
			a=ch-'0';
			push_opnd(opnd,a);
			ch=getchar();
		} 
		else
		{
			fore=*--optr->top;
			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.

六、思考

 如果栈中没有提前放入一个#,会是什么情况?

我的想法:#的优先级最低,这样其与符号相比,都为<,即当前的运算符的优先级比上一个大,不执行。但最后一个一直进不了栈的

  #的优先级也比所有 的运算符的优先级低,所以一直消耗栈中的符号,从而达到清理栈的作用。

  上面是#的作用。

  此时如果开头没有#,则第一个入栈的符号无法比较,需要进行下单独处理,把它放在循环的外面。从第二次起,进行

  上述循环就成了。但多个#,明显简单些。 

猜你喜欢

转载自blog.csdn.net/sinat_38816924/article/details/79242016