• 中缀表达式与后缀表达式


            计算中缀表达式”可以称得上是一个特别经典的关于栈的算法题,几乎在所有数据结构教材中都会涉及,而且很多公司面试或者笔试的时候都会把这道题作为一个考察点。可以说,这是一道必须要掌握的算法题。中缀表达式、后缀表达式等概念在这里就不赘述了,让我们直奔主题。
    题目:输入一个中缀表达式,计算其结果。
    输入的前提假设:
    (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
      
  • 相关阅读:
    第四次寒假作业
    寒假作业三
    寒假作业二
    关于C语言
    寒假作业2代码
    计算机小白
    软工第二次作业
    新开始
    android 自定义滑动按钮
    新知识 HtMl 5
  • 原文地址:https://www.cnblogs.com/zhengxunjie/p/10372329.html
Copyright © 2020-2023  润新知