模拟表达式运算(加减乘除,负数,带括号)
要模拟表达式运算需要目标不同表达式之间的转换关系.
可以参考我的另一篇文章(中后缀表达式转换)
由于水平问题(自己写的总有bug,而且又长又繁琐),所以参考代码选取网上的优秀代码,
但网上的模拟表达式运算要么解释不清楚,要么代码不够完善.
所以我修改了一下网上代码,使其更简单,更完善.
这份代码用数组来表达堆栈.
运算数与运算符用同一个变量p表示栈顶下标.
因为双目运算符是两个元素进行运算,返回一个运算. n个运算数,则有n-1个双目运算符.(只包括双目运算符)
一个运算符对应它右边的运算数.(左括号用0对应,但0不参与运算,具体过程看下面代码)
最后返回1个最终结果.
按步骤分割程序:
(具体解释和细节都添加到注释中)
数据声明(全部采用全局变量):
int number[101];//储存运算数
char symbol[101];//储存运算符
char s[101];//储存字符串
int i=0;//用来遍历字符串
int p=1;//表达栈顶下标,初始化1是为了防止下标越界
入栈操作:
void push()//入栈
{
symbol[++p]=s[i];
//先增加p值然后将运算符置于栈顶.
}
出栈操作:(每输出一个就进行一次运算,就无需储存输出后的运算符)
void pop()//出栈,并顺带完成相应的运算
{//将number[p](栈顶)与number[p-1]进行运算,并将结果储存在number[p-1];
p--;//出栈后,栈顶下标-1;
//number[p]是新的栈顶元素
if(symbol[p+1]=='+')//已经弹出的运算符
{
number[p]+=number[p+1];//运算结果
}else if(symbol[p+1]=='-')
{
number[p]-=number[p+1];
}else if(symbol[p+1]=='*')
{
number[p]*=number[p+1];
}else if(symbol[p+1]=='/')
{
number[p]/=number[p+1];
}
}
返回运算符优先级:(可以加上其它优先级的运算符)
int cmp(char a)//返回运算符优先级
{
if(a=='*'||a=='/')
{
return 2;
}else if(a=='+'||a=='-')
{
return 1;
}else if(a=='(')//入栈的括号优先级最低
{
return 0;
}
return 0;
}
通过优先级比较判断出栈还是入栈:
int can()
{
if(cmp(s[i])<=cmp(symbol[p]))//当栈顶运算符优先级大于等于比较运算符时进行出栈
{
return 1;
}
return 0;
}
转换成数字:(可以用辅助字符数组记录数字,然后用atof转换,就可以处理浮点数)
int sum=0;
while(s[i]>='0'&&s[i]<='9')//转化数字
{
sum=sum*10+s[i++]-'0';
}
number[p]=sum;//设为栈顶运算数(因为下一个一定是运算符),初始化为0.
对运算符进行操作:
do{//一定进行一轮
if(s[i]==')')//遇到右括号,不入栈
{
while(symbol[p]!='(')//不是左括号则出栈
pop();
//栈顶为左括号时
number[--p]=number[p+1];//处理括号对应的0;
//此时symbol[p]为'(',因为'('对于的值是没有赋值的0,所以用栈顶的值代替括号对应的值.
}
else
{
if(s[i]=='-'&&s[i-1]=='*'||s[i-1]=='/')//处理负数 (单目运算符)
{number[p-1]*=-1;//改成负数
i++;
continue;
}
while(can())//一直出栈直到可以入栈
pop();
push();
}
i++;
}while(i<strlen(s)&&s[i-1]==')');//判断是否有多重括号
完全代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int number[101];//储存运算数
char symbol[101];//储存运算符
char s[101];//储存字符串
int i=0;//用来遍历字符串
int p=1;//表达栈顶下标,初始化1是为了防止下标越界
void push()//入栈
{
symbol[++p]=s[i];
//先增加p值然后将运算符置于栈顶.
}
void pop()//出栈,并顺带完成相应的运算
{//将number[p](栈顶)与number[p-1]进行运算,并将结果储存在number[p-1];
p--;//出栈后,栈顶下标-1;
//number[p]是新的栈顶元素
if(symbol[p+1]=='+')//已经弹出的运算符
{
number[p]+=number[p+1];//运算结果
}else if(symbol[p+1]=='-')
{
number[p]-=number[p+1];
}else if(symbol[p+1]=='*')
{
number[p]*=number[p+1];
}else if(symbol[p+1]=='/')
{
number[p]/=number[p+1];
}
}
int cmp(char a)//返回运算符优先级
{
if(a=='*'||a=='/')
{
return 2;
}else if(a=='+'||a=='-')
{
return 1;
}else if(a=='(')
{
return 0;
}
return 0;
}
int can()
{
if(cmp(s[i])<=cmp(symbol[p]))//当栈顶运算符优先级大于等于比较运算符时进行出栈
{
return 1;
}
return 0;
}
int main()
{
gets(s);
s[strlen(s)]=')';
symbol[p]='(';//symbol[1]是括号,防止对空栈操作,同时对应第一个数
while(i<strlen(s))
{
while(s[i]=='(')//优先录入左括号
{
push();
i++;
}
int sum=0;
while(s[i]>='0'&&s[i]<='9')//转化数字
{
sum=sum*10+s[i++]-'0';
}
number[p]=sum;//设为栈顶运算数(因为下一个一定是运算符),初始化为0.
do{//一定进行一轮
if(s[i]==')')//遇到右括号,不入栈
{
while(symbol[p]!='(')//不是左括号则出栈
pop();
//栈顶为左括号时
number[--p]=number[p+1];//处理括号对应的0;
//此时symbol[p]为'(',因为'('对于的值是没有赋值的0,所以用栈顶的值代替括号对应的值.
}
else
{
if(s[i]=='-'&&s[i-1]=='*'||s[i-1]=='/')//处理负数 (单目运算符)
{number[p-1]*=-1;//改成负数
i++;
continue;
}
while(can())//一直出栈直到可以入栈
pop();
push();
}
i++;
}while(i<strlen(s)&&s[i-1]==')');//判断是否有多重括号
}
cout<<number[0]<<endl;
return 0;
}
注意点:
1.开头就自动在首尾添上括号
2.先p++才入栈,同个p,symbo先被赋值
3.遇到左括号则将对应数值"左移".
4.number[0]是最终结果.
该代码运行流程:
案例:5+9/3
这段代码对于初学者来说可能很复杂,可以多调试几遍或画图来加深理解.
由于水平问题,文章中可能存在不足和错误,欢迎评论指出.