栈和队列——表达式求值大全

表达式求值

一.关于三种表达式的分类

  • 中缀表达式:即我们最为常见的表达式,运算符号位于参与运算的连个操作数中间的表达式称作中缀表达式
  • 前缀表达式:前缀表达式是一种没有括号的算术表达式,与中缀表达式不同的是,其将运算符写在前面,操作数写在后面。为纪念其发明者波兰数学家Jan Lukasiewicz,前缀表达式也称为“波兰式”。例如,- 1 + 2 3,它等价于1-(2+3)。
  • 后缀表达式:指的是不包含括号,运算符放在两个运算对象的后面,所有的计算按运算符出现的顺序,严格从左向右进行(不再考虑运算符的优先规则),后缀表达式也称为逆波兰表达式。

二.表达式的求值算法

1.中缀表达式的求值算法:

假设参与运算的表达式是由+ - * \ ( )这6种符号构成的

规定运算符的优先级:

  • 运算符)的优先级最低,
  • 运算符*,\的优先级相同,运算符+,-的优先级相同,且*,\的优先级大于+,-
  • 运算符(的优先级比较特殊,当(作为入栈元素时,它的优先级最大,即无条件入栈;而当(作为栈顶元素时,它的优先级最小,即除运算符)以外的所有运算符无条件入栈
  • 当栈顶元素为(,入栈元素为)时,两运算符在符号栈中相抵消。

假设θ1是栈顶元素,θ2是入栈元素,那么具体的运算符优先级关系比较可由下表体现:
在这里插入图片描述
在求值算法中,我们需要用到两个栈来帮助计算:
其中一个称为运算数栈,用来存放参与运算的数,简称OPND(operand)
另一个称为运算符栈,用来存放参与运算的运算符,简称OPTR(operator)

正式算法描述如下:

首先初始化两个栈,然后从中缀表达式按照从左往右的顺序去取符号c,

如果取到的是运算数,则将c放入OPND中入栈;
如果取到的是运算符,则加以判断:

  • 如果OPTR栈空,则将c入栈OPTR
  • 如果c的优先级大于OPTR的栈顶元素,则将c入栈OPTR
  • 如果c的优先级小于OPTR的栈顶元素,则从栈OPTR中以此出栈两个运算数n1,n2,做n2 c n1的运算后将得到的结果n入栈OPND
  • ‘(’和’)‘的情况可特殊处理,如果栈顶元素和入栈元素分别为’(‘和’)‘时,相消即可

当操作数栈空并且表达式被全部读取完毕时,计算结束,可以获得最终求值结果

下面列出算法代码:

关于栈的定义和基本操作:

//栈的定义 
typedef struct stack
{
	char elements[100];
	int top;
	int size;
}stack;

//栈的基本操作 
void InitStack(stack *s)
{
  s->top=0;
  s->size=0;
}

void Push(stack *s,char c)
{
	s->elements[s->top++]=c;
	s->size++;
}

void Pop(stack *s,char *c)
{
	*c=s->elements[--s->top];
	s->size--;
}

void Top(stack *s,char *c)
{
	*c=s->elements[s->top-1];
}

int isEmpty(stack *s)
{
	if(s->top==0)
	return 1;
	else return 0;
}

求值算法:

//中缀表达式求值算法 
int Calculate_InfixExpression(char *s,int length)
{
	stack OPND,OPTR;
	InitStack(&OPND);
	InitStack(&OPTR);
	int i=0;
	char a,b,c,t,result;
	while(s[i]!=0||!isEmpty(&OPTR))
	{
		c=s[i++];
		if(c>='0'&&c<='9')
		{
			Push(&OPND,c);
		}
		else
		{
			Top(&OPTR,&t);
			if(isEmpty(&OPTR)) 	Push(&OPTR,c);
			else
			{
			
			  switch(compare(t,c))
			   {
				case -1: 
				    {
					  Push(&OPTR,c);break;
				    }
				case 1:
					  {
						Pop(&OPTR,&t);
						Pop(&OPND,&a);
						Pop(&OPND,&b);
						Push(&OPND,opreate(a,b,t));
						i--;
						break;
					  }
				case 0:
					  {
						Pop(&OPTR,&t);break;
					  }		
			     }
		   }
		}
	}	
	Top(&OPND,&result);
	return result-'0';
}

补充算法中的两个函数,运算符优先级的比较函数和操作数的计算函数:

//运算符比较函数 
int compare(char c1, char c2)
{
	if(c1=='('&&c2==')') return 0;
	else if(c1=='('||c2=='(') return -1;
	else if(c1=='*'||c1=='/') return 1;
	else if(c1=='+'||c1=='-')
	{
		if(c2=='*'||c2=='/') return -1;
		else return 1;
	}
}

//运算操作函数
char opreate(char n,char m,char c) 
{
	int a=n-'0';
	int b=m-'0';
	switch(c)
	{
		case '+':return b+a+'0';
		case '-':return b-a+'0';
		case '*':return b*a+'0';
		case '/':return b/a+'0';
	}
}

主函数调用及运算效果:

main()
{
	char s[]="3+2*(7-2)+(2+1)*3";
    int result = Calculate_InfixExpression(s,strlen(s));
    printf("%s = %d",s,result);
}

在这里插入图片描述

2.后缀表达式的求值算法

后缀表达式的求值算法比较简单,利用单独的栈即可完成计算:
从左向右扫描后缀表达式:

  • 如果遇到的是运算数,则入栈
  • 如果遇到的是运算符o,则从栈中依次出栈两个元素:a,b;将b o a 的结果入栈
  • 最终当栈中只剩一个元素时,即为最后所求结果

c语言的算法实现,目的是表达算法逻辑,所以为了简单,只支持单步操作结果在255以内的计算,原因是栈的元素类型是char

int calculate(char *src) 
{
	stack s;
	InitStack(&s);
	int length=strlen(src),i=0;
	char c,a,b,result;
	for(i=0;i<length;i++)
	{
		c=src[i];
		if(c>='0'&&c<='9')
		{
			Push(&s,c);
		}
		else
		{
			Pop(&s,&a);
			Pop(&s,&b);
			Push(&s,opreate(a,b,c));
		}
	}
	
	if(!isEmpty(&s)) Pop(&s,&result);
	
	return result-'0';
	
}

三.中缀表达式到后缀表达式的转换

1.利用栈进行转换的算法

算法描述如下:
我们借助一个操作符栈来进行转换运算,并将转换结果存入一个结果队列当中
从左到右按顺序扫描中缀表达式:

  • 如果扫描到的是运算数,则将运算数直接存入结果队列
  • 如果扫描到的是’(',则直接将其入栈,入栈后其拥有最低的优先级
  • 如果扫描到的是’)‘,则将栈中存在于’('之前的所有运算符依次出栈并入队,‘('也出栈但不入队
  • 如果遇到的是一般运算符,则将其与栈顶运算符比较:如果其优先级高于栈顶运算符的优先级,则将其入栈;否则将栈顶运算符出栈并入队,直到栈顶运算符优先级小于该运算符为止
  • 当扫描结束时,如果运算符栈不为空,则将其中元素全部出栈并入队

最终结果队列中存放的就是转换后的后缀表达式

给出后缀表达式算法的c语言实现:

char *conversion(char * src)
{
	stack s;
	InitStack(&s);
	int length=strlen(src),i=0,j=0,k;
	char c,t,*dst=(char *)malloc(sizeof(char)*length);
	for(i=0;i<length;i++)
	{
		c=src[i];
		if(c>='0'&&c<='9')
		{
			dst[j++]=c;
		}
		else if(c=='(')
		{
			Push(&s,c);
		}
		else if(c==')')
		{
			
			Top(&s,&t);
			while(t!='(')
			{
				Pop(&s,&t);
				if(t!='(')  dst[j++]=t;
				else break;	
			}
		}
		else
		{
			if(isEmpty(&s))
			{
				Push(&s,c);
			}
			else
			{
			Top(&s,&t);
			switch(compare(t,c))
			{
				case -1: Push(&s,c);break;
				case 1: 
				{
					while(compare(t,c)==1)
					{
						Pop(&s,&t);
						dst[j++]=t;
						if(isEmpty(&s)) break;
						Top(&s,&t);
					}
					Push(&s,c);	
				}break;
			}
		    }
		}
	}
	while(!isEmpty(&s))
	{
		
		Pop(&s,&t);
		dst[j++]=t;
	}
   return dst;
}

main()
{
    char * src="2+3*4+(6*8+7)*5";
	char *dst=conversion(src);
	printf("后缀表达式为:%s ",dst);
 } 

运行结果如下:
在这里插入图片描述

  • 9
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值