中缀表达式与后缀表达式

        计算中缀表达式”可以称得上是一个特别经典的关于栈的算法题,几乎在所有数据结构教材中都会涉及,而且很多公司面试或者笔试的时候都会把这道题作为一个考察点。可以说,这是一道必须要掌握的算法题。中缀表达式、后缀表达式等概念在这里就不赘述了,让我们直奔主题。
题目:输入一个中缀表达式,计算其结果。
输入的前提假设:
(1)只考虑+、-、*、/这四种运算符,中缀表达式中只有一种括号:();
(2)输入的中缀表达式中只有整数,没有小数;
(3)假定输入是合法的。
很多文章或课本喜欢一步到位,直接讨论如何从中缀表达式计算结果。但对于初学者来说,跨度未免大了点。这里循序渐进,从易到难,先讨论如何将中缀表达式转化为后缀表达式,再讨论如何计算后缀表达式。最后在前面两步的基础上,讨论如何一步到位,直接计算中缀表达式的结果:
一、如何将中缀表达式转化为后缀表达式
        在日常应用中,算术表达式中运算符总是出现在两个操作数之间,例如5*(7-2*3)+8/2,这种形式称为中缀表达式。计算一个中缀表达式需要知道运算符的优先级和结合性。乘除是高优先级,加减是低优先级,优先级相同时他们都是左结合的,也就是从左计算到右。有括号就要计算括号内的表达式。
中缀表达式利于人的理解,但不便于计算机的处理。因此需要将中缀表达式转换成后缀表达式,以方便计算机处理。所谓后缀表达式就是将运算符放在运算数之后。后缀表达式也称为逆波兰表达式。
比如:
中缀表达式为:1+(2-3)*4+4/2
对应后缀表达式为:1 2 3 - 4* + 4 2 / +
如何将一个中缀表达式转化为后缀表达式?我们需要借助栈的力量,用它来存放运算符。算法流程如下:
首先将各种运算符(包括括号)的优先级排列如下(数字越大,优先级越高):
1:(
2:+ -
3:* /
4:)
对输入的中缀表达式从左到右遍历:
1)如果遇到数字,直接添加到后缀表达式末尾;
2)如果遇到运算符+、-、*、/:
先判断栈是否为空。若是,则直接将此运算符压入栈。若不是,则查看当前栈顶元素。若栈顶元素优先级大于或等于此操作符级别,则弹出栈顶元素,将栈顶元素添加到后缀表达式中,并继续进行上述判断。如果不满足上述判断或者栈为空,将这个运算符入栈。要注意的是,经过上述步骤,这个运算符最终一定会入栈。
3)如果遇到括号:
如果是左括号,直接入栈。如果是右括号,弹出栈中第一个左括号前所有的操作符,并将左括号弹出。(右括号别入栈)。
4)字符串遍历结束后,如果栈不为空,则弹出栈中所有元素,将它们添加到后缀表达式的末尾,直到栈为空。
二、计算后缀表达式
后缀表达式的计算就相当简单了。准备一个数字栈。从左到右扫描后缀表达式,如果是数字,放入数字栈。如果是符号,从数字栈中弹出两个数字,第一个取出的数字为右运算数,第二个为左运算数,进行运算。然后将结果放进数字栈中。如此反复,直到读完整个表达式后,留在数字栈中的那个数字就是最终结果。
C++代码如下,要注意,下面的代码默认中缀表达式中所有数字都是整数,并且都在0到9之间。而且计算结果都是整数(比如5/2=2)。

#include<iostream>  
#include<string>  
#include<stack>  
  
using namespace std;  
  
int getPriority(char ch)  
{  
    //获取优先级  
    if (ch == '(') return 1;  
    else if (ch == '+' || ch == '-') return 2;  
    else if (ch == '*' || ch == '/') return 3;  
    else return 4;  
}  
  
string getPostfixExpression(string str)  
{  
    //将中缀表达式转化为后缀表达式  
    //默认输入是合法的  
    stack<char> mystack;  
    int size = str.size();  
    int i = 0;  
    char tmp;  
    string res = "";  
    while (i < size) {  
        if (str[i] >= '0' && str[i] <= '9'){  
            res.push_back(str[i]);  
        }  
        elseif (str[i] == '+' || str[i] == '-' || str[i] == '*' || str[i] == '/') {  
            if (mystack.empty()) {  
                mystack.push(str[i]);  
            }  
            else {  
                while (!mystack.empty()) {  
                    tmp = mystack.top();  
                    if (getPriority(tmp) >= getPriority(str[i])) {  
                        //弹出栈顶元素  
                        res.push_back(tmp);  
                        mystack.pop();  
                    }  
                    else break;  
                }  
                mystack.push(str[i]);  
            }  
        }  
        else {  
            if(str[i]=='(') mystack.push(str[i]);  
            else {  
                while (mystack.top() != '(') {  
                    tmp = mystack.top();  
                    res.push_back(tmp);  
                    mystack.pop();  
                }  
                mystack.pop();  
            }  
        }  
        i++;  
    }  
  
    //遍历完后,若栈非空,弹出所有元素  
    while (!mystack.empty()) {  
        tmp = mystack.top();  
        res.push_back(tmp);  
        mystack.pop();  
    }  
    return res;  
}  
  
   
  
int calculator(string str)  
{  
    //计算后缀表达式的值,默认中缀表达式所有数字都是一位的,在0-9之间  
    stack<int> mystack;  
    int size = str.size();  
    int num1, num2, num3;  
    for (int i = 0; i < size; i++) {  
        if (str[i] >= '0' && str[i] <= '9') {  
            mystack.push(str[i] - '0');  
        }  
        else {  
            num2 = mystack.top();  
            mystack.pop();  
            num1 = mystack.top();  
            mystack.pop();  
            if (str[i] == '+') {  
                num3 = num1 + num2;  
            }  
            else if (str[i] == '-') {  
                num3 = num1 - num2;  
            }  
            else if (str[i] == '*') {  
                num3 = num1 * num2;  
            }  
            else if (str[i] == '/') {  
                num3 = num1 / num2;  
            }  
            mystack.push(num3);  
        }  
    }  
    return mystack.top();  
}  
  
   
int main()  
{  
    string str="1+(2-3)*4+4/2";  
    cout <<"中缀表达式为:"<< endl << str << endl;  
    string res = getPostfixExpression(str);  
    cout <<"后缀表达式为:"<< endl << res << endl;  
    int num_res = calculator(res);  
    cout <<"计算结果:"<< endl << num_res << endl;  
    system("pause");  
    return 0;  
}  

三、直接计算中缀表达式
其实将前面的两步结合起来,就可以得到直接计算的方法。准备一个数字栈和一个符号栈。
从左到右遍历中缀表达式。如果遇到数字,入数字栈。
如果遇到符号(四个运算符以及括号),跟前面的“中缀表达式转后缀表达式”过程一样,对符号栈进行处理。处理过程中,对每一个出栈的运算符:+ - * /,根据“计算后缀表达式”的方法,计算结果(跟数字栈配合)。
如果遍历完中缀表达式后符号栈还非空,就继续出符号栈的运算符,计算,直到符号栈为空。最后数字栈剩下的数字就是结果。
下面给出用C++实现“计算中缀表达式”的代码,里面考虑了“数字不止1位”,并且用浮点型来表示最终运算结果。要求中缀表达式中只能包含整数和运算符(不能包含小数),并且是合法的。

#include<iostream>  
#include<string>  
#include<stack>  
  
using namespace std;  
  
int getPriority(char ch)  
{  
    //获取优先级  
    if (ch == '(') return 1;  
    else if (ch == '+' || ch == '-') return 2;  
    else if (ch == '*' || ch == '/') return 3;  
    else return 4;  
}  
  
void calculate(stack<double> &mystack, char operation)  
{  
    double num1, num2, num3;  
    num2 = mystack.top();  
    mystack.pop();  
    num1 = mystack.top();  
    mystack.pop();  
    if (operation == '+') {  
        num3 = num1 + num2;  
    }  
    else if (operation == '-') {  
        num3 = num1 - num2;  
    }  
    else if (operation == '*') {  
        num3 = num1 * num2;  
    }  
    else if (operation == '/') {  
        num3 = num1 / num2;  
    }  
  
    mystack.push(num3);  
}  
  
double calculator(string str)  
{  
    //计算中缀表达式,默认输入是合法的  
    stack<double> mystack_number;  
    stack<char> mystack_operation;  
    int i = 0, j;  
    int size = str.size();  
    char tmp_operation;  
    string tmp_num;  
    while (i < size) {  
        if (str[i] >= '0' && str[i] <= '9') {  
            j = i;  
            while (j < size && str[j] >= '0' && str[j] <= '9') { j++; }  
            tmp_num = str.substr(i, j - i);  
            mystack_number.push(atoi(tmp_num.c_str()));  
            i = j;  
        }  
        else if (str[i] == '+' || str[i] == '-' || str[i] == '*' || str[i] == '/') {  
            if (mystack_operation.empty()) {  
                mystack_operation.push(str[i]);  
            }  
            else {  
                while (!mystack_operation.empty()) {  
                    tmp_operation = mystack_operation.top();  
                    if (getPriority(tmp_operation) >= getPriority(str[i])) {  
                        //计算  
                        calculate(mystack_number, tmp_operation);  
                        mystack_operation.pop();  
                    }  
                    else break;  
                }  
                mystack_operation.push(str[i]);  
            }  
            i++;  
        }  
        else {  
            if (str[i] == '(') mystack_operation.push(str[i]);  
            else {  
                while (mystack_operation.top() != '(') {  
                    tmp_operation = mystack_operation.top();  
                    //计算  
                    calculate(mystack_number, tmp_operation);  
                    mystack_operation.pop();  
                }  
                mystack_operation.pop();  
            }  
            i++;  
        }  
  
    }  
    //遍历完后,若栈非空,弹出所有元素  
    while (!mystack_operation.empty()) {  
        tmp_operation = mystack_operation.top();  
        //计算  
        calculate(mystack_number, tmp_operation);  
        mystack_operation.pop();  
    }  
    return mystack_number.top();  
}  
  
int main()  
{  
    string str = "1+(2-3)*4+10/2+2*2+2+2/5";  
    cout << "中缀表达式为:" << endl << str << endl;  
    double num_res = calculator(str);  
    cout << "计算结果:" << endl << num_res << endl;  
    system("pause");  
    return 0;  
}  
相信通过这篇文章,大家对这个问题会有所了解。
给出一道思考题:如果加入乘方'^',应该如何处理?要注意,乘方运算是右结合的。
其实很简单。只有两处修改:
1)将乘方添加到优先级中:
1:(
2:+ -
3:* /
4:^
5:)
2)在读中缀表达式的时候,如果读到乘方^,就将它放进符号栈中。因为乘方的优先级是最高的,而且是右结合的,所以无论它前面出现的是什么运算,这些运算都不能执行。而且它本身能否执行也是不知道的,因此只能进栈。
【参考资料】
https://blog.csdn.net/sinat_27908213/article/details/80273557
2019-02-13
23:09:01
  

猜你喜欢

转载自www.cnblogs.com/zhengxunjie/p/10372329.html