这个程序的编写本质上主要是栈的应用。我的程序主要分为2部分。第一部分为把输入的中缀表达式转换为后缀表达式,第二部分为计算后缀表达式。2个部分都用一个栈来辅助操作并检查表达式的是否合法。
(1)、中缀表达式转换为后缀表达式:
函数int infix_to_postfix(const string& infix, string& postfix);执行中缀表达式转换为后缀表达式的计算。它使用一个栈来存储遇到的操作符和左括号,遇到操作数则直接输出。
操作符在入栈的时候要比较栈顶符号和待入栈符号的优先级,优先级高的操作符意味着它要先计算,所以要先输出。
当优先级相同时,(a)如果是待入栈操作符是左结合性,则先输出栈顶的那个操作符,这样直到栈顶符号的优先级小于当前待入栈的符号为止。然后把待入栈的操作符压入栈中;(b)如果待入栈操作符是右结合性,则要把操作符直接入栈。比如在本程序中,我们对^预算符就是这么处理的。
在转换过程中,还有一个比较特殊的符号,那就是括号。括号在中缀表达式中是用来确定计算先后顺序的,但是在后缀表达式中,它就不再需要了。计算的顺序是通过后缀表达式的性质来保证的。把一对括号中的中缀表达式转换为后缀表达式,然后把括号直接去掉就可以了。所以对括号的处理就是:遇到左括号,和其他的运算符一样,把比它优先级还要高的运算符弹出来(但是遇到左括号时就要停止了),然后把左括号入栈。遇到右括号时,则出栈,即把一对括号之间的中缀表达式已经转为后缀表达式了。当左括号为栈顶的时候,停止出栈循环,然后弹出栈顶的左括号。
(2)计算后缀表达式:
int compute_postfix(const string& postfix_expr, double& result);用来计算前面产生的后缀表达式。这个过程比较简单,遇到操作数时,直接入栈;由于在后缀表达式中,操作符都是在它的操作数后面的,所以遇到操作符时,就从栈中取出这个操作符需要的操作数然,进行必要的检查后,就可以进行计算了。计算结果再次压入栈中,供后面使用。当后缀表达式处理完毕后,最后的结果就是栈顶了。
上面介绍的两个部分都用到了int getToken(const string& expr, string& token, size_t& idx);函数。这个函数的作用是:把输入的表达式切分为一个个的操作符或者操作数,供使用。
代码如下:
- #define _CRT_SECURE_NO_WARNINGS // for MS crt.
- #include <fstream>
- #include <stack>
- #include <string>
- #include <iomanip>
- #include <cmath>
- using namespace std;
- bool isLetter(char c);
- bool isOperator(char c);
- bool isBracket(char c);
- int getToken(const string& expr, string& token, size_t& idx);
- int getOperatorPriority(const string& op);
- int infix_to_postfix(const string& infix, string& postfix);
- int compute_postfix(const string& postfix_expr, double& result);
- int main()
- {
- ifstream cin("input.txt"); // input
- ofstream cout("output.txt"); // output
- string infix_expr, postfix_expr;
- double result = 0.0;
- while (getline(cin, infix_expr)) {
- if (infix_to_postfix(infix_expr, postfix_expr)) {
- if (compute_postfix(postfix_expr, result)) {
- cout << fixed << setprecision(2) << result << endl;
- continue ; // have no error, so next expr.
- }
- }
- cout << "ERROR IN INFIX NOTATION" << endl;
- }
- return 0;
- }
- int infix_to_postfix(const string& infix_expr, string& postfix_expr)
- {
- string token;
- size_t idx = 0;
- stack<string> stk;
- int balance = 0; // use to check the bracket's balance.
- postfix_expr.clear();
- while (getToken(infix_expr, token, idx)) {
- switch (token[0]) {
- /* If we see '+','-','*','/','%','^','(' , then we pop entries from
- * the stack until we find an entry of lower priority
- * One exception is that we never remove a '(' from the stack except when processing a ')'.*/
- case '+':
- case '-':
- case '*':
- case '/':
- case '%':
- case '(':
- if (token[0] == '(')
- ++balance;
- while (!stk.empty() && getOperatorPriority(stk.top()) >= getOperatorPriority(token) && stk.top() != "(") {
- postfix_expr += stk.top();
- stk.pop();
- postfix_expr += " ";
- }
- stk.push(token);
- break;
- /* right association, handle it specifically! */
- case '^':
- while (!stk.empty() && getOperatorPriority(stk.top()) > getOperatorPriority(token) && stk.top() != "(") {
- postfix_expr += stk.top();
- stk.pop();
- postfix_expr += " ";
- }
- stk.push(token); // later come, early out.(right association)
- break;
- /* If we see a ')', then we pop the stack, writing symbols until we encounter
- * a (corresponding) left parenthesis, which is poped but not output. */
- case ')':
- --balance;
- while (!stk.empty() && stk.top() != "(") {
- postfix_expr += stk.top();
- stk.pop();
- postfix_expr += " ";
- }
- stk.pop();
- break;
- default:
- postfix_expr += token;
- postfix_expr += " ";
- break;
- }
- }
- while (!stk.empty()) {
- postfix_expr += " ";
- postfix_expr += stk.top();
- stk.pop();
- }
- if (balance != 0) {
- return 0;
- }
- return 1;
- }
- int compute_postfix(const string& postfix_expr, double& result)
- {
- stack<string> stk;
- string token;
- size_t idx = 0;
- double operand1 = 0.0, operand2 = 0.0;
- char buf[128] = {0};
- int resStatus = 0;
- while (getToken(postfix_expr, token, idx)) {
- memset(buf, 0, 128 * sizeof(char));
- switch (token[0]) {
- case '+':
- case '-':
- case '*':
- case '/':
- case '%':
- case '^':
- // firstly, get two operands to compute.
- operand1 = atof(stk.top().c_str());
- stk.pop();
- operand2 = atof(stk.top().c_str());
- stk.pop();
- switch (token[0]) {
- case '+':
- sprintf(buf, "%f", operand1 + operand2);
- break;
- case '-':
- sprintf(buf, "%f", operand2 - operand1);
- break;
- case '*':
- sprintf(buf, "%f", operand1 * operand2);
- break;
- case '/':
- if (!operand1) {
- resStatus = 0;
- goto Exit;
- }
- sprintf(buf, "%f", operand2 / operand1);
- break;
- case '%':
- // if operand is a float number, then error!
- if ((int)operand1 != operand1 || (int)operand2 != operand2 || !operand1) {
- resStatus = 0;
- goto Exit;
- }
- // care: the format should be "%d".
- sprintf(buf, "%d", (int)operand2 % (int)operand1);
- break;
- case '^':
- if (operand2 <= 0) {
- resStatus = 0;
- goto Exit;
- }
- sprintf(buf, "%f", pow(operand2, operand1));
- break;
- }
- stk.push(string(buf));
- break;
- default:
- stk.push(token); // numbers push into the stack directly.
- break;
- }
- }
- // now the number in the stack is the result.
- result = atof(stk.top().c_str());
- resStatus = 1;
- Exit:
- return resStatus;
- }
- int getOperatorPriority(const string& op)
- {
- int priority = -1;
- switch (op[0])
- {
- case '(':
- priority = 4;
- break;
- case '+':
- case '-':
- priority = 1;
- break;
- case '*':
- case '/':
- case '%':
- priority = 2;
- break;
- case '^':
- priority = 3;
- break;
- }
- return priority;
- }
- int getToken(const string& expr, string& token, size_t& idx)
- {
- char curChar = 0;
- bool inTok = false ;
- int res = 0;
- token.clear();
- size_t len = expr.length();
- while (idx < len) {
- curChar = expr[idx++];
- if (isspace(curChar) || isOperator(curChar) || isBracket(curChar)) {
- if (!inTok && (isOperator(curChar) || isBracket(curChar))) {
- token += curChar;
- res = 1; // token is a operatro or bracket, return token immediately.
- break;
- }
- if (inTok) {
- if (isOperator(curChar) || isBracket(curChar))
- --idx; // back input pointer and get the operator or bracket when we call getToken() next time.
- res = 1;
- break;
- }
- inTok = false;
- } else if (isLetter(curChar)) { // letter is illegal.
- res = 0;
- break;
- } else if (!inTok) { // begin a number.
- inTok = true;
- token += curChar;
- } else if (inTok) {
- token += curChar;
- }
- }
- if (!token.empty())
- res = 1;
- return res;
- }
- bool isLetter(char c)
- {
- if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')
- return true;
- return false;
- }
- bool isOperator(char c)
- {
- if (c == '+' || c == '-' || c == '*' || c == '/' || c == '%' || c == '^')
- return true ;
- return false ;
- }
- bool isBracket(char c)
- {
- if (c == '(' || c == ')')
- return true;
- return false;
- }