假设我们有一个便携计算器并想要计算一趟外出购物的花费。为此,我们将一列数据相加并将结果乘以1.06,它是所购物品的价格以及附加的地方税。如果购物的各项花销为4.99,5.99和6.99,那么输入这些数据的自然的方式是
4.99 + 5.99+ 6.99 × 1.06 =
随着计算器的不同,这个结果或者是所要的答案19.05,或者是科学答案18.39。最简单的四功能计算器将给出第一个答案,但是许多先进的计算器是知道乘法的优先级是高于加法的。
另一方面,有些项是需要上税的而有些项则不是,因此,如果只有第一项和最后一项是要上税的,那么
4.99×1.06 + 5.99 + 6.99×1.06 =
将在科学计算器上给出正确的答案18.69而在简单计算器上给出的错误的答案19.37。科学计算器一般包括括号,因此我们总可以通过加括号的方法得到正确的答案,但是使用简单计算器我们需要记住中间结果。
该列的典型计算顺序可以是将4.99和1.06相乘并存为A1,然后将5.99和A1相加,再将结果存入A1。我们再将6.99和1.06相乘并将答案存为A2,最后将A1和A2相加并将最后结果放入A1。我们可以将这种操作顺序书写如下:
4.99 1.06 × 5.99 + 6.99 1.06 × +
这个记法叫做后缀(postfix)或逆波兰(reverse Polish)记法,其求值过程恰好就是我们上面所描述的过程。计算这个问题最容易的方法就是使用一个栈,当见到一个数时就把它推入栈中,再遇到一个运算符时就作用与从该栈中弹出的两个数上,将所得结果推入栈中。
代码如下:
#include #include #include using std::vector;using std::stack;using std::string;using std::endl;using std::cout;double getPostfixResult(string& PostfixStr){ vector items; string item; for(const auto &chr : PostfixStr){ if((chr >= '0' && chr <= '9') || chr == '.'){ item.push_back(chr); } if(chr == ' ' && !item.empty()){ items.emplace_back(item); item.clear(); } if(chr == '+' || chr == '-' || chr == '*' || chr == '/'){ items.emplace_back(string(1, chr)); } } stack myStack; for(const auto &item : items){ try{ double num = std::stod(item); myStack.push(num); } catch(std::exception e){ double num1 = myStack.top(); myStack.pop(); double num2 = myStack.top(); myStack.pop(); if(item == "+"){ myStack.push(num2 + num1); } if(item == "-"){ myStack.push(num2 - num1); } if(item == "*"){ myStack.push(num2 * num1); } if(item == "/"){ myStack.push(num2 / num1); } } } return myStack.top();}int main(int argc, char const *argv[]){ string testStr = "4.99 1.06 * 5.99 + 6.99 1.06 * +"; cout << "result: " << getPostfixResult(testStr) << endl; return 0;}
计算一个后缀表达式花费的时间是O(N),因为对输入中的每个元素处理都是由一些栈操作组成从而花费时间。该算法的计算是非常简单。注意:当一个表达式以后缀记号给出时,没有必要知道任何优先规则。这是一个明显的优点。