逆波兰式

逆波兰式

定义

一个表达式E的后缀形式可以如下定义:

(1)如果E是一个变量或常量,则E的后缀式是E本身。

(2)如果E是E1 op E2形式的表达式,这里op是如何二元操作符,则E的后缀式为E1’E2’ op,这里E1’和E2’分别为E1和E2的后缀式。

(3)如果E是(E1)形式的表达式,则E1的后缀式就是E的后缀式。

如:我们平时写a+b,这是中缀表达式,写成后缀表达式就是:ab+

(a+b)*c-(a+b)/e的后缀表达式为:

(a+b)*c-(a+b)/e

→((a+b)*c)((a+b)/e)-

→((a+b)c*)((a+b)e/)-

→(ab+c*)(ab+e/)-

→ab+c*ab+e/-

作用:

实现逆波兰式的算法,难度并不大,但为什么要将看似简单的中序表达式 转换为复杂的逆波兰式?原因就在于这个简单是相对人类的思维结构来说的,对计算机而言中序表达式是非常复杂的结构。相对的,逆波兰式在计算机看来却是比较简单易懂的结构。因为计算机普遍采用的内存结构是栈式结构,它执行先进后出的顺序。

算法实现

将一个普通的中序表达式转换为逆波兰表达式的一般算法是:

首先需要分配2个栈,一个作为临时存储运算符的栈S1(含一个结束符号),一个作为输入逆波兰式的栈S2(空栈),S1栈可先放入优先级最低的运算符#,注意,中缀式应以此最低优先级的运算符结束。可指定其他字符,不一定非#不可。从中缀式的左端开始取字符,逐序进行如下步骤:

(1)若取出的字符是操作数,则分析出完整的运算数,该操作数直接送入S2栈

(2)若取出的字符是运算符,则将该运算符与S1栈栈顶元素比较,如果该运算符优先级(不包括括号运算符)大于S1栈栈顶运算符优先级,则将该运算符进S1栈,否则,将S1栈的栈顶运算符弹出,送入S2栈中,直至S1栈栈顶运算符低于(不包括等于)该运算符优先级,最后将该运算符送入S1栈。

(3)若取出的字符是“(”,则直接送入S1栈顶。

(4)若取出的字符是“)”,则将距离S1栈栈顶最近的“(”之间的运算符,逐个出栈,依次送入S2栈,此时抛弃“(”。

(5)重复上面的1~4步,直至处理完所有的输入字符

(6)若取出的字符是“#”,则将S1栈内所有运算符(不包括“#”),逐个出栈,依次送入S2栈。

完成以上步骤,S2栈便为逆波兰式输出结果。不过S2应做一下逆序处理。便可以按照逆波兰式的计算方法计算了!

class TransformReversePolishNotation {
private:
    stack<char> temp;
    stack<char> rpn;
    unordered_map<char, int> dict;
    void init() {
        //初始化字典
        dict['#'] = -1;
        dict['+'] = 1;
        dict['-'] = 1;
        dict['*'] = 2;
        dict['/'] = 2;
        dict['('] = 0;
        dict[')'] = 0;
    }
public:
    stack<char> pn2rpn(string s) {
        stack<char> result;
        init();
        temp.push('#');
        for (int i = 0; i < s.size(); ++i) {
            if (dict.count(s[i]) == 0) {//如果这个字符不存在
                rpn.push(s[i]);//将操作数入栈
            } else if (s[i] == '(') {//直接进temp中
                temp.push(s[i]);
            }else if (s[i] == ')') {//将(间的元素放入pn,删除(
                while(temp.top()!='(') {//当temp的栈顶不为(且,当前运算符的优先级小于或等于栈顶
                    rpn.push(temp.top());//将temp栈顶元素放进pn中
                    temp.pop();//将栈顶元素出栈
                }
                temp.pop();//删除'('
            } else {//各种运算符
                for (;dict[s[i]] <= dict[temp.top()];temp.pop()) {//当temp的栈顶不为(且,当前运算符的优先级小于或等于栈顶
                    rpn.push(temp.top());//将temp栈顶元素放进pn中
//                    temp.pop();//将栈顶元素出栈
                }
                if(dict[s[i]] > dict[temp.top()])
                    temp.push(s[i]);
            }
        }
        for(;dict[temp.top()]>0;temp.pop())
            rpn.push(temp.top());//将temp栈顶元素放进pn中

        //遍历并逆序
        for (; rpn.size() > 0; rpn.pop())
            result.push(rpn.top());
        return result;
    }
};

计算方法

新建一个表达式,如果当前字符为变量或者为数字,则压栈,如果是运算符,则将栈顶两个元素弹出作相应运算,结果再入栈,最后当表达式扫描完后,栈里的就是结果。

举例

下面以(a+b)c为例子进行说明: (a+b)c的逆波兰式为ab+c,假设计算机把ab+c按从左到右的顺序压入栈中,并且按照遇到运算符就把栈顶两个元素出栈,执行运算,得到的结果再入栈的原则来进行处理,那么ab+c的执行结果如下: 1)a入栈(0位置) 2)b入栈(1位置) 3)遇到运算符“+”,将a和b出栈,执行a+b的操作,得到结果d=a+b,再将d入栈(0位置) 4)c入栈(1位置) 5)遇到运算符“”,将d和c出栈,执行d*c的操作,得到结果e,再将e入栈(0位置) 经过以上运算,计算机就可以得到(a+b)*c的运算结果e了。 逆波兰式除了可以实现上述类型的运算,它还可以派生出许多新的算法,数据结构,这就需要灵活运用了。逆波兰式只是一种序列体现形式。

简单实现:10以内的式子

class TransformReversePolishNotation {
private:
    stack<char> temp;
    stack<char> rpn;
    unordered_map<char, int> dict;

    void init() {
        //初始化字典
        dict['#'] = -1;
        dict['+'] = 1;
        dict['-'] = 1;
        dict['*'] = 2;
        dict['/'] = 2;
        dict['('] = 0;
        dict[')'] = 0;
    }

    int computeRPN(stack<char> rpn) {
        init();
        vector<int> count;
        while (rpn.size() > 0) {
            for (; rpn.size() > 0 && dict.find(rpn.top()) == dict.end(); rpn.pop()) {//如果栈顶是操作数
                int num = (int) rpn.top() - 48;
//            int number = atoi(num);//转换字符串的,用来处理123+1213等
                count.push_back(num);
            }
            switch (rpn.top()) {
                case '+':
                    count[count.size() - 2] += count[count.size() - 1];
                    count.pop_back();
                    rpn.pop();
                    break;
                case '-':
                    count[count.size() - 2] -= count[count.size() - 1];
                    count.pop_back();
                    rpn.pop();
                    break;
                case '*':
                    count[count.size() - 2] *= count[count.size() - 1];
                    count.pop_back();
                    rpn.pop();
                    break;
                case '/':
                    count[count.size() - 2] =
                            count[count.size() - 1] != 0 ? count[count.size() - 2] * count[count.size() - 1] : count[
                                    count.size() - 2];
                    count.pop_back();
                    rpn.pop();
                    break;
                default:
                    cout<<"不支持的运算类型";

            }

        }


        return count[0];
    }
};

leetcode 150:逆波兰式计算: 题解:

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        if (tokens.empty()) return 0;
        stack<long long int> st;
        for (auto token : tokens) {
            if (token == "+" || token == "-" || token == "*" || token == "/") {
                int b = st.top();
                st.pop();
                int a = st.top();
                st.pop(); 
                if (token == "+") {
                    st.push(a+b);
                } else if (token == "-") {
                    st.push(a-b);
                } else if (token == "*") {
                    st.push(a*b);
                } else if (token == "/") {
                    st.push(a/b);
                }
            } else {
                st.push(stoi(token));
            }
        }
        return (int)st.top();
    }
};

CC BY-NC 4.0

Flask入门
语料处理基础

Comments