描述
输入一个表达式(用字符串表示),求这个表达式的值。
保证字符串中的有效字符包括[‘0’-‘9’],‘+’,‘-’, ‘*’,‘/’ ,‘(’, ‘)’,‘[’, ‘]’,‘{’ ,‘}’。且表达式一定合法。
数据范围:表达式计算结果和过程中满足 ∣val∣≤1000 ,字符串长度满足 1≤n≤1000
输入描述:
输入一个算术表达式
输出描述:
得到计算结果
示例1
输入:
3+2*{1+2*[-4/(8-6)+7]}
输出:
25
我觉得这道题很难,涉及到的知识点有《数据结构》中栈在表达式求值中的应用。
解题思路:
我们平时见到的表达式求值,是中缀表达式,例如计算A+B= ;这种操作符都在两个操作数之间的表达式,也就是中缀表达式。然而计算机世界中,并不是根据中缀表达式求值,而是根据后缀或前缀表达式求值,我们知道中缀表达式求值,是因为我们知道运算的优先级,先括号,再乘除,后加减....,计算机却并不知道,于是有大佬发明了前缀和后缀表达式,计算机直接根据表达式就可以求解,并不涉及运算符的优先级(ps:其实如果要算的表达式很长很繁琐的话,我们用中缀表达式计算可能还没有用前缀和后缀表达式计算来的快)。
说了这么多,那什么是前缀和后缀表达式,举个例子,计算A+B,其后缀表达式就是AB+,而前缀表达式就是+AB,很容易理解。
上图的第一行是我们常见的中缀表达式,第二行就是转换得到的后缀表达式了,标的序号就是从左到右的运算顺序,后缀表达式的运算符也是以这个顺序出现的。
这是我们人工转换的后缀表达式,那计算机是怎么将中缀表达式转换成后缀表达式的?
中缀转后缀:
初始化一个栈,用于保存暂时还不能确定运算顺序的运算符。
从左到右处理各个元素,直到末尾。可能遇到三种情况:
1.遇到操作数。直接加入后缀表达式。
2.遇到界限符。遇到"("直接入栈;遇到")"则依次弹出栈内运算符并加入后缀表达式,直到弹出"("为止。注意"("不加入后缀表达式。
3.遇到运算符。依次弹出栈中优先级高于或等于当前运算符的所有运算符,加入后缀表达式,若碰到"("或栈空则停止。之后再把当前运算符入栈。
按上述方法处理完所有字符后,将栈中剩余运算符依次弹出,并加入后缀表达式。
由于运算符属于字符型,而操作数是绝对值不大于1000的整数类型,转换成后缀表达式的存放是个问题。笔者开辟了两个数组,分别用来存放操作符和操作数,比如“56+32”转换成后缀是“56 32+”,我将操作数放在整型数组num中,为{56,32,2000},这里不存在操作数的地方都填2000,因为题目要求运算结果和运算过程中的值的绝对值不大于1000,我们后期看到2000,就知道这个地方没有操作数,而操作符存储在字符数组o中{'!','!','+'},在没有操作符的地方全填'!'。
将中缀转成后缀后,就是利用后缀表达式进行运算从而求出结果了
利用后缀表达式求值
只需要遍历操作符数组,依次进行运算便可。
例如上式,先扫描到'+',然后从后往前遍历'+'位置之前的操作数,弹出最近的两个操作数进行相加即可,相加结果保存到原来A在的位置,同时令B=2000,表示这个位置不再是操作数,然后将用过的操作符'+'赋值为'!',表示这个位置不再是操作符,依次按照扫描到的操作符进行计算即可。
运算完成后,遍历操作数的数组,找出其中不等于2000的数,就是计算结果。
代码如下所示:
#include <stdio.h>
#define N 1000
#define MAX 2000
typedef struct operator
{
char ope; //操作符
int pri; //优先级
}operator;
int main()
{
char str[N],o[N];
operator temp[N]; //temp数组用来暂存操作符
int num[N],t=0,j,cal[2];
int i=0,pos=0,pri,flag=0,cnt=0;
gets(str);
while(str[i]!='\0')
{
if(str[i]=='['||str[i]=='{')
{
str[i]='(';
}
else if(str[i]==']'||str[i]=='}')
{
str[i]=')';
}
i++;
}
for(j=0;j<i;j++)
{
o[j]='!'; //先把操作符全都初始化为'!'
num[j]=MAX; //把操作数全部初始为不可能出现的最大值
}
//中缀表达式转成机算的后缀表达式
i=0;
while(str[i]!='\0')
{
if(str[i]>='0'&&str[i]<='9')
{
t*=10;
t+=str[i]-'0';
flag=1;
}
else if(str[i]=='+'||str[i]=='-'||str[i]=='*'||str[i]=='/')
{
if(flag==1)
{
num[pos++]=t;
t=0;flag=0;
}
if(str[i]=='*'||str[i]=='/')
{
pri=1;
}
else if(str[i]=='+'||str[i]=='-')
{
pri=0;
if(str[i]=='-'&&str[i-1]=='(')
{
num[pos++]=0;
}
}
while(temp[cnt-1].pri>=pri&&temp[cnt-1].ope!='('&&cnt!=0)
{
o[pos++]=temp[cnt-1].ope;
cnt--;
}
temp[cnt].ope=str[i];
temp[cnt].pri=pri;
cnt++;
}
else if(str[i]=='(')
{
temp[cnt++].ope=str[i];
}
else if(str[i]==')')
{
if(flag==1)
{
num[pos++]=t;
t=0;flag=0;
}
while(temp[cnt-1].ope!='(')
{
o[pos++]=temp[cnt-1].ope;
cnt--;
}
if(temp[cnt-1].ope=='(')
{
cnt--;
}
}
i++;
}
if (flag==1)
{
num[pos++] = t;
}
while (cnt>0)
{
o[pos++]=temp[--cnt].ope;
}
//根据后缀表达式求出结果
for(i=0;i<pos;i++)
{
if(o[i]!='!')
{
cal[0]=cal[1]=MAX;
for(j=i-1;j>=0;j--)
{
if(num[j]!=MAX)
{
if(cal[1]==MAX)
{
cal[1]=num[j];
num[j]=MAX;
}
else
{
cal[0]=num[j];
break;
}
}
}
if(o[i]=='+')
num[j]=cal[0]+cal[1];
else if(o[i]=='-')
num[j]=cal[0]-cal[1];
else if(o[i]=='*')
num[j]=cal[0]*cal[1];
else if(o[i]=='/')
num[j]=cal[0]/cal[1];
}
o[i]='!';
}
for(i=0;i<pos;i++)
{
if(num[i]!=MAX)
{
printf("%d\n",num[i]);
break;
}
}
return 0;
}