中缀表达式求值
中缀表达式就是我们平常所见的数学式子 :5+3 6+5*8 -3*(1-9) 等等
这类表达式的特点就是运算符与操作数有特定的规则 如"+" 加数+加数 、 ‘-’ 被减数 -减数 等等 一般来说运算符在操作数中间
这类表达式我们可以直接计算 ,但计算机计算却有些麻烦了
所以我们必须设计一个合适有效的算法来让计算机计算这些表达式
一种方式是 运用两个栈 一个存储操作符称为OPTR_Stack 另一个存储操作数称为OPAN_Stack
规则:
依次读入 c
1.c是操作数 则压入操作数栈.。
2.若c是操作符且 是非‘)’和‘(’的操作符 ,将c与操作符栈中栈顶的操作符比较优先级
(1)若优先级 栈顶 <=c 则将c压入运算符栈中。
(2)若优先级 栈顶 > c 则将栈顶运算符弹出 并将运算数栈 中弹出两个字符 计算并将结果压入运算符栈,直至遇到左括号或者遇到满足(1)条件 把c 压入运算符栈中。
3.若是'(’ 压入栈中。
4. 若c是‘')' 则将运算符栈 中栈顶的操作符弹出,并将运算数栈 中弹出两个字符 计算并将结果压入运算符栈,直至遇到'(',将'('中栈中删除 开始读取下一字符。
5.若到字符串结束 则将运算符栈顶的操作符弹出 ,并将运算数栈 中弹出两个字符 计算并将结果压入运算符栈,直至栈空
原理就是如果这个操作符优先级低的话 先计算前面优先级高的
比如计算 6*5+3 当读入6 *5后 读入+ 必须要将前面运算符级别高的'*'计算得出6*5=30 才能将后面的30与3相加。
代码如下
/*
中缀表达式求值
*/
#include<stdio.h>
#include<iostream>
#include<string>
#include<string.h>
#include<algorithm>
#include<stack>
#define ERROR 0x7f7f7f7f
using namespace std;
const int maxn=1100;
int Priority(char c)//运算符的优先级
{
if(c=='(')
return 0;
if(c=='+'||c=='-')
return 1;
return 2;
}
void Preprocess(char *str,const char *s)//预处理将字符串转化为正数加减乘除
{
int top=0;
int len=strlen(s);
for(int i=0; i<len; i++)
{
if(s[i]==' '||s[i]=='=')
continue;
if(s[i]=='-'&&(i==0||s[i-1]=='('))//将-8+10转化为 0-8+10
str[top++]='0';
if(s[i]=='('&&(s[i-1]>='0'&&s[i-1]<='9')&&i>=1)//对于8(6-10) 变为8*(6-10)
str[top++]='*';
str[top++]=s[i];
}
str[top]=0;
}
double calcu(double num1,double num2,char c)//计算
{
double res;
switch(c)
{
case '+':
res=num1+num2;
break;
case '-':
res=num1-num2;
break;
case '*':
res=num1*num2;
break;
case '/':
res=num1/num2;
break;
}
return res;
}
double GetMidExpreVal(const char *s)//将中缀表达式转化为后缀表达式
{
/*
操作数直接加入表
操作符 、
if '(' 加入栈
')' 取出直至'('
运算符若优先级大加入 否则弹出
*/
const char* user_read=s;
stack<char>OPTR;//运算符
stack<double>OPAN;
while(*user_read!=' ')
{
if((*user_read<='9'&&*user_read>='0')||*user_read=='.')//运算数
{
double dble;
dble=atof(user_read);
while((*user_read<='9'&&*user_read>='0')||*user_read=='.')//将指针移动到第一个非数字位置
{
user_read++;
}
OPAN.push(dble);
}
else if(*user_read=='(')
OPTR.push(*user_read++);
else if(*user_read==')')
{
while(OPTR.top()!='(')
{
char now_optr;
double num1,num2;
now_optr=OPTR.top();
OPTR.pop();
num2=OPAN.top();
OPAN.pop();
num1=OPAN.top();
OPAN.pop();
OPAN.push(calcu(num1,num2,now_optr));
/*
每次弹出一个操作符 两个操作数 将结果压入栈中
*/
}
OPTR.pop();
user_read++;
}
else
{
if(OPTR.empty())//特判如果栈中没运算符就直接加入(看成左括号)
{
OPTR.push(*user_read++);
continue;
}
char now_optr=OPTR.top();
if(Priority(*user_read)>Priority(now_optr))
OPTR.push(*user_read++);
else
{
OPTR.pop();
double num1,num2;
num2=OPAN.top();
OPAN.pop();
num1=OPAN.top();
OPAN.pop();
OPAN.push(calcu(num1,num2,now_optr));
}
}
}
while(!OPTR.empty())
{
char now_optr;
double num1,num2;
now_optr=OPTR.top();
OPTR.pop();
num2=OPAN.top();
OPAN.pop();
num1=OPAN.top();
OPAN.pop();
OPAN.push(calcu(num1,num2,now_optr));
}
return OPAN.top();
}
int main()
{
int t;
char s[maxn],str[maxn];
scanf("%d",&t);
while(t--)
{
scanf("%s",s);
Preprocess(str,s);
printf("%0.2lf
",GetMidExpreVal(str));
}
}
另一种方式就是:将中缀表达式转化为前缀表达式或者后缀表达式
前缀表达式 运算符在操作数前面 6+5 前缀表达式为+ 6 5
后缀表达式 运算符在操作数后面 3*5 后缀表达式 3 5 *
转化成前缀 或者后缀表达式的方便之处就是不用考虑运算符的优先级问题
转化为后缀表达式的过程其实就是上面利用中缀表达式求值的过程
过程不在描述 可以戳这里
代码:
/*
中缀表达式转换为后缀表达式
*/
#include<stdio.h>
#include<iostream>
#include<string>
#include<string.h>
#include<algorithm>
#include<stack>
#define ERROR 0x7f7f7f7f
using namespace std;
const int maxn=1100;
int Priority(char c)//运算符的优先级
{
if(c=='(')
return 0;
if(c=='+'||c=='-')
return 1;
return 2;
}
void Preprocess(char *str,const char *s)//预处理将字符串转化为正数加减乘除
{
int top=0;
int len=strlen(s);
for(int i=0; i<len; i++)
{
if(s[i]==' '||s[i]=='=')
continue;
if(s[i]=='-'&&(i==0||s[i-1]=='('))//将-8+10转化为 0-8+10
str[top++]='0';
if(s[i]=='('&&(s[i-1]>='0'&&s[i-1]<='9')&&i>=1)//对于8(6-10) 变为8*(6-10)
str[top++]='*';
str[top++]=s[i];
}
str[top]=0;
}
void Mid_toBehExpress(char *str,const char *s)//将中缀表达式转化为后缀表达式
{
/*
操作数直接加入表
操作符 、
if '(' 加入栈
')' 取出直至'('
运算符若优先级大加入 否则弹出
*/
const char* user_read=s;
stack<char>OPTR;//运算符
while(*user_read!=' ')
{
if((*user_read<='9'&&*user_read>='0')||*user_read=='.')//运算数
{
while((*user_read<='9'&&*user_read>='0')||*user_read=='.')//将指针移动到第一个非数字位置
{
*str++=*user_read++;
}
*str++=' ';
}
else if(*user_read=='(')
OPTR.push(*user_read++);
else if(*user_read==')')
{
while(OPTR.top()!='(')
{
*str++=OPTR.top();
*str++=' ';
OPTR.pop();
}
OPTR.pop();
user_read++;
}
else
{
if(OPTR.empty())
{
OPTR.push(*user_read++);
continue;
}
char now_optr=OPTR.top();
if(Priority(*user_read)>Priority(now_optr))
OPTR.push(*user_read++);
else
{
OPTR.pop();
*str++=now_optr;
*str++=' ';
}
}
}
while(!OPTR.empty())
{
*str++=OPTR.top();
OPTR.pop();
*str++=' ';
}
*str=' ';
}
int main()
{
int t;
char s[maxn],str[maxn];
scanf("%d",&t);
while(t--)
{
scanf("%s",s);
Preprocess(str,s);
Mid_toBehExpress(s,str);
cout<<s<<"="<<endl;
}
}
计算中缀表达式有一个细节是一个'-' 怎么判断出他是符号还是减号
当时看了一个博客https://www.cnblogs.com/dolphin0520/p/3708602.html觉得很好
但是对于-(-8)这样的式子就处理不了
今天无意间用WIN10的计算器 突然发现 如果你输入-8 他会自动在前面加一个0 变为0-8
这样就把负数转化为 两个正数的加减法了
.