[经典算法] 中序式转后序式/前序式
题目说明:
平常所使用的运算式,主要是将运算元放在运算子的两旁,例如a+b/d这样的式子,这称之为中序(Infix)表示式,对于人类来说,这样的式子很容易理 解,但由于电脑执行指令时是有顺序的,遇到中序表示式时,无法直接进行运算,而必须进一步判断运算的先后顺序,所以必须将中序表示式转换为另一种表示方 法。
可以将中序表示式转换为后序(Postfix)表示式,后序表示式又称之为逆向波兰表示式(Reverse polish notation),它是由波兰的数学家卢卡谢维奇提出,例如(a+b)*(c+d)这个式子,表示为后序表示式时是ab+cd+*。
题目解析:
用手算的方式来计算后序式相当的简单,将运算子两旁的运算元依先后顺序全括号起来,然后将所有的右括号取代为左边最接近的运算子(从最内层括号开始),最后去掉所有的左括号就可以完成后序表示式,例如:
a+b*d+c/d => ((a+(b*d))+(c/d)) -> bd*+cd/+
如果要用程式来进行中序转后序,则必须使用堆叠,演算法很简单,直接叙述的话就是使用回圈,取出中序式的字元,遇运算元直接输出,堆叠运算子与左括号, ISP>ICP的话直接输出堆叠中的运算子,遇右括号输出堆叠中的运算子至左括号。
以下是虚拟码的运算法,\0表示中序式读取完毕:
Procedure Postfix(infix) [ Loop [ op = infix(i) case [ :x = '\0': while (stack not empty) // output all elements in stack end return :x = '(': // put it into stack :x is operator: while (priority(stack[top]) >= priority(op)) [ // out a element from stack ] // save op into stack :x = ')': while ( stack(top) != '(' ) [ // out a element from stack ] top = top - 1 // not out '( :else: // output current op ] i++; ] ]
例如(a+b)*(c+d)这个式子,依演算法的输出过程如下:
OP |
STACK |
OUTPUT |
( |
( |
- |
a |
( |
a |
+ |
(+ |
a |
b |
(+ |
ab |
) |
- |
ab+ |
* |
* |
ab+ |
( |
*( |
ab+ |
c |
*( |
ab+c |
+ |
*(+ |
ab+c |
d |
*(+ |
ab+cd |
) |
* |
ab+cd+ |
- |
- |
ab+cd+* |
如果要将中序式转为前序式,则在读取中序式时是由后往前读取,而左右括号的处理方式相反,其余不变,但输出之前必须先置入堆叠,待转换完成后再将堆叠中的 值由上往下读出,如此就是前序表示式。
程序代码:
#include <iostream> #include <stack> #include <algorithm> #include <gtest/gtest.h> using namespace std; int GetOperatorPrior(char value) { int nResult = 0; switch(value) { case '+': case '-': { nResult = 1; } break; case '*': case '/': { nResult = 2; } break; } return nResult; } bool ConvertToPostfix(const string& infixExp, string& postfixExp) { postfixExp.clear(); stack<int> Operators; for (string::size_type i = 0; i < infixExp.size(); ++i) { char cValue = infixExp[i]; switch(cValue) { case '(': { Operators.push(cValue); } break; case ')': { while(!Operators.empty() && (Operators.top() != '(')) { postfixExp += Operators.top(); Operators.pop(); } Operators.pop(); } break; case '+': case '-': case '*': case '/': { while (!Operators.empty() && (GetOperatorPrior(Operators.top()) >= GetOperatorPrior(cValue)) ) { postfixExp += Operators.top(); Operators.pop(); } Operators.push(cValue); } break; default: postfixExp += cValue; break; } } while(!Operators.empty()) { postfixExp += Operators.top(); Operators.pop(); } return true; } bool ConvertToPrefix(const string& infixExp, string& prefixExp) { prefixExp.clear(); int* Stack = new int[infixExp.size()+1]; int nTop = 0; for (int i = infixExp.size() - 1; i >= 0; --i) { char cValue = infixExp[i]; switch (cValue) { case ')': { Stack[++nTop] = ')'; } break; case '(': { while (nTop && Stack[nTop] != ')') { prefixExp += Stack[nTop]; nTop--; } nTop--; } break; case '+': case '-': case '*': case '/': { while (nTop && GetOperatorPrior(Stack[nTop]) >= GetOperatorPrior(cValue)) { prefixExp += Stack[nTop]; nTop--; } Stack[++nTop] = cValue; } break; default: prefixExp += cValue; break; } } while (nTop) { prefixExp += Stack[nTop--]; } reverse(prefixExp.begin(), prefixExp.end()); return true; } TEST(Algo, tInFixPostfix) { // // Postfix Convert // // a+b*d+c/d => abd*+cd/+ string strResult; ConvertToPostfix("a+b*d+c/d",strResult); ASSERT_EQ("abd*+cd/+",strResult); // (a+b)*c/d+e => ab+c*d/e+ ConvertToPostfix("(a+b)*c/d+e",strResult); ASSERT_EQ("ab+c*d/e+",strResult); // ((a)+b*(c-d)+e/f)*g => abcd-*+ef/g* ConvertToPostfix("((a)+b*(c-d)+e/f)*g",strResult); ASSERT_EQ("abcd-*+ef/+g*",strResult); // // Prefix Convert // // a+b*d+c/d => +a+*bd/cd ConvertToPrefix("a+b*d+c/d",strResult); ASSERT_EQ("+a+*bd/cd",strResult); // (a+b)*c/d+e => +*+ab/cde ConvertToPrefix("(a+b)*c/d+e",strResult); ASSERT_EQ("+*+ab/cde",strResult); // ((a)+b*(c-d)+e/f)*g => *+a+*b-cd/efg ConvertToPrefix("((a)+b*(c-d)+e/f)*g",strResult); ASSERT_EQ("*+a+*b-cd/efg",strResult); }