1. 目的:写一个程序,从键盘输入一个合法的四则运算表达式,输出计算结果;
2. 实现原理:将表达式先由中缀式转为后缀式,然后利用栈计算表达式结果;
3. 实现代码:
版本1:
#include <iostream>
#include <stack>
#include <string>
using namespace std;
const int SIZE = 100;
bool isOperator( char op )
{
switch(op)
{
case '+':
case '-':
case '*':
case '/':
return true;
default:
return false;
}
}
int priority( char op ) // 比较运算符优先级
{
int value = -1;
switch(op)
{
case '#':
value = -1;
break;
case '(':
value = 0;
break;
case '+':
case '-':
value = 1;
break;
case '*':
case '/':
value = 2;
break;
}
return value;
}
// 中缀式转换为后缀式
// 把中缀表达式转换为后缀表达式,返回后缀表达式的长度(包括空格)
int preToPostExp( string exp, char postExp[SIZE], int &len )
{
stack<char> opSt;
opSt.push('#');
int i = 0;
int j = 0;
while( exp[i] != '#' )
{
if( exp[i] >= '0' && exp[i] <= '9' || exp[i] == '.' )
{
postExp[j++] = exp[i];
len++;
}
else if( exp[i] == '(' )// 遇到“(”不用比较直接入栈
{
opSt.push(exp[i]);
}
else if( exp[i] == ')' )//遇到右括号将其对应左括号后的操作符(操作符栈中的)全部写入后缀表达式
{
while( opSt.top() != '(' )
{
postExp[j++] = opSt.top();
opSt.pop();
len++;
}
opSt.pop(); //将'('出栈 将“(”出栈,后缀表达式中不含小括号
}
else if( i == 0 && (exp[i] == '+' || exp[i] == '-') ) //表明第一个数为正负号
{
postExp[j++] = exp[i];
len++;
}
else if( isOperator(exp[i]) )
{
postExp[j++] = ' '; //用空格隔开操作数
len++;
//当前的操作符小于等于栈顶操作符的优先级时,将栈顶操作符写入到后缀表达式,重复此过程
while( priority(exp[i]) <= priority( opSt.top() ) )
{
postExp[j++] = opSt.top();
opSt.pop();
len++;
}
opSt.push(exp[i]); // 当前操作符优先级大于栈顶操作符的优先级,将该操作符入栈
}
i++;
}
while( opSt.top() != '#' ) // 将所有的操作符加入后缀表达式
{
postExp[j++] = opSt.top();
opSt.pop();
len++;
}
return 0;
}
double read_number(char str[],int *i)
{
double x=0.0;
int k = 0;
while(str[*i] >='0' && str[*i]<='9') // 处理整数部分
{
x = x*10+(str[*i]-'0');
(*i)++;
}
if(str[*i]=='.') // 处理小数部分
{
(*i)++;
while(str[*i] >= '0'&&str[*i] <='9')
{
x = x * 10 + (str[*i]-'0');
(*i)++;
k++;
}
}
while(k!=0)
{
x /= 10.0;
k--;
}
return x;
}
// 计算后缀表达式结果
double computePostExp( char post[SIZE] )
{
stack<double> stack; // 操作数栈
double x1 = 0;
double x2 = 0;
bool flag = false;
int i = 0;
double d = 0;
while( post[i] != '\0' )
{
if( post[i] >= '0' && post[i] <= '9' )
{
d = read_number(post,&i);
if(flag) // 第一个数为负数
{
d = -d;
flag = false;
}
stack.push(d);
}
else if(post[i] == ' ' )
i++;
else if (post[i] =='+')
{
x2 = stack.top();
stack.pop();
x1 = stack.top();
stack.pop();
stack.push(x1+x2);
i++;
}
else if( post[i] == '-' && i == 0 ) //表明第一个数为负数,方便计算-1+2*3
{
flag = true;
i++;
}
else if (post[i] =='-')
{
x2 = stack.top();
stack.pop();
x1 = stack.top();
stack.pop();
stack.push(x1-x2);
i++;
}
else if (post[i] =='*')
{
x2 = stack.top();
stack.pop();
x1 = stack.top();
stack.pop();
stack.push(x1*x2);
i++;
}
else if (post[i] =='/')
{
x2 = stack.top();
stack.pop();
x1 = stack.top();
stack.pop();
stack.push(x1/x2);
i++;
}
}
return stack.top();
}
int main()
{
string exp = "";
char postExp[SIZE]; // 保存后缀式结果
cout << "输入表达式(中缀,以#结束):";
cin >> exp; // 以#号结束
int len = 0;
preToPostExp( exp, postExp, len );
postExp[len] = '\0';
cout << "后缀表达式为:";
cout << postExp << endl;
cout << "\n由后缀表达式计算出的数值结果: ";
cout << computePostExp(postExp) << endl;
system("pause");
return 0;
}
/*中缀式变后缀式算法:
反复读取输入内容直到结束为止
如果是数字直接入栈
否则如果是'('就等待处理括号里面的
否则如果是')'就倒回来处理直到左括号为止的全部运算
否则就是普通运算符(+,-,*,/)
反复看那些暂存的未处理的运算符,只要优先级不比本运算符低就处理(入栈)
暂存本运算符
反复取出栈中的剩余运算符并保存,直到栈无运算符
*/
4. 结果:
转载地址:
http://blog.csdn.net/geekcoder/article/details/6829386
注:相对于转载的地址中的代码,改进之一就是能够计算第一个操作数为负数的情况,即计算类似:-1+2*3的表达式
版本2:
#include <iostream>
#include <stack>
#include <string>
using namespace std;
/*
表达式计算思路一:
1)准备两个栈:数据栈和运算符栈;
反复读取表达式:(2,3,4步)
2)如果是数,入数据栈;
3)如果是左括号,入运算符栈,如果是右括号,反复从运算符栈顶取运算符和
从数据栈里取两个数据进行计算,并把结果入数据栈,直到遇到栈顶是左括号为止。
4)如果是运算符op,先跟栈顶的运算符比,只要不高于栈顶优先级,就取出栈顶
的运算符和数据栈的两个数据进行计算,并把结果入数据栈,直到高于栈顶运算符优
先级或者遇到左括号或者运算符栈空为止,此时把op入栈;
5)处理栈中的运算符:取出栈顶的运算符和数据栈的两个数据进行计算,并把结果入数据
栈,直到运算符栈空为止;
6)这时数据栈中的数据就是计算结果。
*/
class Exp
{
private:
stack<char> ops;
stack<double> ds;
double v, lh, rh; // 计算结果和临时操作数变量
char op; //运算符
public:
double calinput()//读取并计算表达式直到结束为止
{
do
{
readdata();
skipspace(); //跳过空白
}
while( readop() );
calremain(); //处理栈中剩余的运算符
return v;
}
void readdata() //在该出现数据的时候可能出现'('
{
// 如1+((2*3)) 左括号可能是多个
while( !(cin>>v) ) //读取数据失败,此时读取应该是'(',而v为double类型
{
cin.clear();
cin >> op; //读取'('
if( op != '(' )
{
throw string("在该出现数值的地方出现了") + op;
}
ops.push(op); //左括号入栈
}
ds.push(v);
return;
}
void skipspace()
{
while( cin.peek() == ' ' || cin.peek() == '\t' ) //cin.peek只看不取走
{
cin.ignore(); //丢掉一个空白字符
}
}
bool readop() //读取运算符,可能出现右括号或者换行符(表达式结束)
{
//如(2*(3+4/(4-5))),右括号可能是多个
while( (op = cin.get() ) == ')' )
{
while( ops.top() != '(' )
{
rh = ds.top(); //取左操作数
ds.pop();
lh = ds.top();//取右操作数
ds.pop();
ds.push( cal( lh, ops.top(), rh ) ); //计算并入栈
ops.pop(); //取走运算符
}
ops.pop(); //丢弃'('
}
if( op == '\n' )
{
return false;
}
if( strchr("+-*/", op ) == NULL ) //无效运算符
{
throw string("无效运算符") + op;
}
while( !ops.empty() && ops.top() != '(' && !prior( op, ops.top() ) )
{
rh = ds.top(); ds.pop();
lh = ds.top(); ds.pop();
ds.push( cal( lh, ops.top(), rh ) ); //计算并入栈
ops.pop(); //取走运算符
}
ops.push(op);
return true;
}
void calremain()
{
while( !ops.empty() )
{
rh = ds.top(); ds.pop();
lh = ds.top(); ds.pop();
ds.push( cal( lh, ops.top(), rh ) ); //计算并入栈
ops.pop(); //取走运算符
}
if( ds.size() != 1 )
{
throw string("无效的表达式");
}
v = ds.top();
ds.pop();
return;
}
double cal( double lh, char op, double rh )
{
return op == '+'?lh+rh:op=='-'?lh-rh:op=='*'?lh*rh:lh/rh;
}
bool prior( char o1, char o2 )//若o1比o2优先级高返回true,否则返回false
{
return o1 != '+' && o1 != '-' && o2 != '*' && o2 != '/';
}
};
int main()
{
Exp e;
try
{
cout << e.calinput() << endl;
}
catch( const string &e )
{
cout << e << endl;
}
system("pause");
return 0;
}
输出结果: