当前位置:文档之家› C++四则运算表达式求值算法

C++四则运算表达式求值算法

C++ 四则运算表达式求值算法
作者:韦延昭 Email: yls8420@
26,April 26, 2008
引言:四则运算表达式包含操作数、操作符以及括号等。操作数
可以是任意实数,操作符包括加( + )、减(- )、乘(* )以及 除( / )等。本文将利用 C++标准模板库(STL)中的堆栈和队列 来编程实现计算任意实数的四则运算表达式的值。
问题:设计一个处理和计算四则表达式的类,该类有一个字符串
类型的成员变量用来表示初始的中缀表达式的字符串,例如中缀表达 式:
(2.99-4.32)*(90.8-78.66)+78.0/3.14 通过该类的成员函数将该表达式的值计算出来。 要求:操作数可以是任意实数、括号对数以及表达式长度没有限 制。
分析:由于操作数是任意的实数,所以必须将原始的中缀表达式
que.push(str); } else { if(str.size() != 0)
que.push(str); str = ch; que.push(str);
str = ""; } }
return que; }
6)ChangeToSuffix()函数。将保存在队列中的中缀表达式转换为后 缀表达式,并保存在栈中。这个函数也是整个表达式算法的关键,这 里需要两个栈 stack_A 和 stack_B,分别在转换过程中临时存放后 缀表达式的操作数与操作符。依次从中缀表达式队列 que 中出列一 个元素,并保存在一个 string 变量 str 中,然后按以下几方面进行 处理: ①如果 str 是“(”,则将 str 推入栈 stack_B。 ②如果 str 是“)”,则要考虑 stack_B 的栈顶是不是“(”,是的话就 将“(”出栈 stack_B;如果不是,则将 stack_B 出栈一个元素(操 作符),然后将其推入栈 stack_A。 ③如果 str 是“+”或“-”,则要考虑有括号和优先级的情况,如果 栈 stack_B 为空或者栈顶为“(”,则将 str 推入栈 stack_B;因为操 作符“+”和“-”优先级相同(谁先出现就先处理谁进栈 stack_A), 并且低于“*”和“/”,所以当栈 stack_B 不为空并且栈顶不为“(”, 则依次循环取出 stack_B 的栈顶元素并依次推入栈 stack_A,循环 结束后再将 str 推入栈 stack_B。 ④如果 str 是“*”或“/”,因为它们的优先级相同并且高于“+”和 “-”,所以如果栈 stack_B 为空或者栈顶为“+”、“-”或“(”就直 接将 str 推入栈 stack_B;否则就将 stack_B 弹出一个元素并将其
的字符串。
u 这里定义了两个构造函数,不带参数的默认 m_string 为空,带 string 类型
参数的将参数值赋予成员变量 m_string,此外还定义了赋值运算符“=”,
目 的是将一个表达式字符串 赋予 成员变量 m_string 。 因此 生 成一个 ExpressionType 的对象可以有如下两种形式: ExpressionType expr(“(2.99-4.32)*(90.8-78.66)+78.0/3.14”); 或者 ExpressionType expr; Expr = “(2.99-4.32)*(90.8-78.66)+78.0/3.14”; u DivideExpressionToItem(): 私有成员函数,返回值是一个 string 类型的 队列。其功能是将原始的中缀表达式中的操作数、操作符以及括号按顺序以 字符串的形式分解出来,然后保存在一个队列中(队列的操作规则是先进先 出)。 u ChangeToSuffix(): 私有成员函数,返回值是一个 string 类型的栈。其功 能是将队列中表示原始表达式各项的字符串调整顺序,转换成后缀表达式的 顺序,并处理掉括号,然后保存在一个栈中(栈的操作规则是先进后出)。 u IsWellForm():私有成员函数,返回 bool 值。其功能是判断原始表达式中的 括号是否匹配,如果匹配返回 true,否则返回 false。 u Size():返回原始表达式所包含的字节数。 u Calculate(): 公有成员函数,返回 double 类型的值。其功能是计算转换后 的后缀表达式的值,即原始中缀表达式的值。
ch = m_string.at(i); switch(ch) { case '(': stack.push(ch);
break; case ')': if(stack.empty())
return false; else stack.pop(); break; default:break; } } return stack.empty(); }
queue<string> que;
if(!IsWellForm())// 括号是否匹配 { cout<<"The original expression is not well-form. Please check it and try
again!"<<endl; return que; }
string str = "";
5)DivideExpressionToItem()函数。分解出原始中缀表达式中的操
作数、操作符以及括号,保存在队列中,分解完成后队列中的保存顺
序如下图所示:
图 1 分解表达式
其 算 法 思想 是: 从左 往 右按 一个字 节 依次 扫描 原 始中缀表达式
m_string,碰到连续的数字或小数点就加到 string 变量 str 中;如
bNumber = true; break; case '(': case ')': case '+': case '-': case '*': case '/': bNumber = false; break; default:continue; }
if(bNumber) { str += ch; if(i==size-1)
类 ExpressionType 的定义包含了三个头文件<string>、<stack>
以及<queue>,这是标准模板库里现成的东西,直接拿来用就行了,
不必再自己写 栈和队列, 它们都 在 std 命名空间里 。 下面对类
ExpressionType 的成员作介绍:
u m_string: 私有成员变量,字符串类型。主要用于存储表示原始中缀表达式
法在类中实现。首先设计一个类 ExpressionType,其具体定义如下:
/**************************************************************************************** * ExpressionType.h ****************************************************************************************/ #include <string> #include <stack> #include <queue> using namespace std;
果碰到括号或操作符就将原先的 str 推入队列,然后将括号或操作符
赋予 str,再将 str 推入队列,并将 str 赋予空值,依次循环进行直
到扫描 m_string 完成。其具体实现代码如下:
queue<string> ExpressionType::DivideExpressionToItem() {
中的操作数、操作符以及括号分解出来,并以字符串的形式保存;然 后再将其转换为后缀表达式的顺序,为什么要转换为后缀表达式的形
式呢?原因是后缀表达式可以很容易地利用堆栈计算出表达式的值。 例如有如下的中缀表达式:
a+b-c 转换成后缀表达式为:
ab+c然后分别按从左到右放入栈中,如果碰到操作符就从栈中弹出两个操 作数进行运算,最后再将运算结果放入栈中,依次进行直到表达式结 束。如上述的后缀表达式先将 a 和 b 放入栈中,然后碰到操作符“+”, 则从栈中弹出 a 和 b 进行 a+b 的运算,并将其结果 d(假设为 d) 放入栈中,然后再将 c 放入栈中,最后是操作符“-”,所以再弹出 d 和 c 进行 d-c 运算,并将其结果再次放入栈中,此时表达式结束,则 栈中的元素值就是该表达式最后的运算结果。当然将原始的中缀表达 式转换为后缀表达式比较关键,要同时考虑操作符的优先级以及对有 括号的情况下的处理,相关内容会在算法具体实现中详细讨论。
推入栈 stack_A 中,然后再将 str 推入栈 stack_B 中。 ⑤除了上述情况 外就只剩下操作数 了,操作数 就可以 直接推 入栈 stack_A 中。注意整个过程中栈 stack_B 中的元素只能是如下几种: “(”、“+”、“-”、“*”、“/”,而最终栈 stack_A 保存的是后缀表达式, 只有操作数和操作符,如下图所示:
4)IsWellForm()函数。判断原始中缀表达式的括号是否匹配,可以
利用栈简单实现,即遇到“(”进栈,遇到“)”就从栈中弹出一个元
素,直到表达式结束。如果栈为空则表示括号匹配,否则不匹配。算
法实现如下:
bool ExpressionType::IsWellForm() { stack<char> stack; int size = Size(); char ch; for(int i = 0; i < size; i++) {
相关主题