LeetCode 224. Basic Calculator(infix to postfix)

链接:https://leetcode.com/problems/basic-calculator/,使用的是通用的后缀表达式方法,也可以解答227. Basic Calculator II
对表达式求值,本质是为操作符operator(简写opt)找到操作数operand(简写opd)。对于中缀表达式,一个opt的左opd由左部分的某个suffix组成,右opd由右部分的某个prefix组成,当prefix suffix都找到后,可以将它们整体视为一个新opd进行之后的处理,如对a+b*c-d,当确定*左opd b,右opd c后,表达式可以换成a+e-d, e=b*c,+的后继由原左opd变成了新opd,-的前驱也由原右opd变成了新opd。(重点)而要判断opt的opd,实际上要以opd为中心,根据优先级和结合性看它属于左右哪个opt。

分析

以a + b * c - d为例,先不考虑括号,按照infix to postfix的算法处理,并探究深层的思想。我们维护两个栈,一个opd栈,一个opt栈,从左往右分析。

opd: a
opt: +

a进opd栈,再看+,a左边没有opt,所以a肯定是+的左opd,但a的右opd还不确定,可能只是b,也可能是b开头的子表达式。将+入栈,此时opd栈顶是opt栈顶的左opd,且右opd还未出现

opd: a b
opt: +      current: *

opd: a b
opt: + *

b进入opd栈,opt栈顶+,opd栈顶b和current *组成表达式中的+ b *,+优先级低于*,b一定是*的左opd。将*入栈,此时同样处于opd栈顶是opt栈顶左opd,而右opd未出现的状态,且a是+的左opd,b是+的后继

opd: a b c
opt: + *    current: -

表达式:a + (bc*) - d
opd: a bc*
opt: +      current: -

表达式:(abc*+) - d
opd: abc*+
opt: -

c进opd栈,再看-,opt栈顶*,opd栈顶和current -组成表达式中* c -,*优先级高于-,c是*右opd,而opd次栈顶(不知道有没有这么个说法,为了方便表述自创的,即栈顶下面那个元素)b是*左opd,这么一来*的opd都确认了,将*与bc都出栈,再组成opd新栈顶,即原表达式的b * c将转化为一个整体bc*。

此时opd栈顶(bc*)是opt栈顶+后继,是current -前驱,对应表达式中+ (bc*) - ,由于左结合,(bc*)是+的右opd,而opd次栈顶a是+左opd,确认后将+与a(bc*)组成opd新栈顶。opd只剩一个了,肯定是-的左opd,-入opt栈。

opd: abc*+ d
opt: -

opd: abc*+d-
opt:

d进opd栈,后面再没有opt,所以opd栈顶肯定是opt栈顶的右opd,(abc*+) - d组合成abc*+d-

思考

两个栈想维护的是这样一种状态:opd[i]是opt[i]的左opd,opd[i+1]是opt[i]在表达式中的直接后继,即opt[i]右opd的第一个组成元素。当栈顶opt[i]的右opd找到后,会和opt[i] opd[i]组成新的opd[i]。而新的opd[i]也会继续充当opt[i-1]的直接后继。

opd: a b c d
opt: x y z u

处理opt之后处于此状态,两栈数量相同,一一对应

opd: a b c d e
opt: x y z u       current: v

再进一个opd e,遇到opt v,u e v在表达式中相邻。

# priority(u) < priority(v) or stack opt is empty
opd: a b c d e
opt: x y z u v

当priority(u) < priority(v),或e之前的这些都不存在,即opt栈空时,e是v的左opd,将v进opt栈,得到目标状态。

# priority(u) > priority(v), including left associated
opd: a b c (deu)
opt: x y z       current: v

如果priority(u) > priority(v),e是u的右opd,已知d是u的左opd,将deu结合进opd栈。此时(deu)将顶替d成为z的后继,也顶替e成为v的前驱。(重点)

收尾

opd: a b c
opt: x y           no more opt

opd: a bcy
opt: x

当分析到表达式末尾,显然opd栈顶c是opt栈顶y的右opd,byc组合成opd新栈顶bcy。依此类推,两栈不断收缩,直到opd只剩一个元素,即表达式值。

带括号

括号相当于具体圈出一个子表达式整体求值,进入括号前和出括号后,差别只在于opd栈多一个值,即此子表达式的结果。遇到open paren,相当于两个栈视为空;遇到close paren,相当于分析完一个表达式,进行收尾工作,当出栈到open paren时结束。

代码

class Solution {
public:
    int calculate(string s) {
        vector<string> postOrder;
        vector<char> opts{'#'}; // opt栈底放一个优先级最低的#,避免讨论空栈
        // 构造后序表达式
        for(int i = 0; i < s.size(); i++) {
            char c = s[i];
            if(c == ' ') continue;
            else if(isdigit(c)) { // 注意不全是个位数
                int cnt = 1, prev = i;
                while(isdigit(s[i+1])) {
                    cnt++; i++;
                }
                postOrder.push_back(s.substr(prev, cnt));
             } else if(c == '(') {
                // '('直接进栈
                opts.push_back(c); 
            } else if(c == ')') {
                // pop until encountering open paren, then discard all two parens
                while(opts.back() != '(') {
                    postOrder.push_back(string(1, opts.back()));
                    opts.pop_back();
                }
                opts.pop_back(); 
            } else {
                // pop until the priority of opt stack top is less
                while(isLarger(opts.back(), c)) {
                    postOrder.push_back(string(1, opts.back()));
                    opts.pop_back();
                }
                opts.push_back(c);
            }
        }
        while(opts.back() != '#') {
            postOrder.push_back(string(1, opts.back()));
            opts.pop_back();
        }
        // 计算后序表达式
        stack<int> stk;
        for(auto &str : postOrder) {
            if(isdigit(str[0])) {
                stk.push(stoi(str));
            } else {
                int tmp, opd2 = stk.top();
                stk.pop();
                int opd1 = stk.top();
                stk.pop();
                if(str == "+")
                    tmp = opd1 + opd2; 
                 else if(str == "-") 
                    tmp = opd1 - opd2;
                 else if(str == "*") 
                    tmp = opd1 * opd2;
                 else if(str == "/") 
                    tmp = opd1 / opd2;
                stk.push(tmp);
            }
        }
        return stk.top();
    }
private:
    // return true, if l's priority is larger than r
    bool isLarger(char l, char r) {
        switch(l) {
            case '#': return false;
            case '(': return false;
            case '*': return true;
            case '/': return true;
            case '+': {
                if(r == '*' || r == '/') return false;
                 return true;
            }
            case '-': {
                if(r == '*' || r == '/') return false;
                 return true;
            }
            default: return true;
        }
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值