python数据结构之栈——应用(2)中缀表达式计算与函数调用栈

    前面一部分学习的是括号匹配和后缀表达式的计算,后缀表达式是没有括号的,每遇到一个运算符就弹出两个元素计算。那么如果遇到含有括号/各种优先级的中缀表达式,如何运算呢?当然还是离不开栈的应用啦。

Leetcode 224. Basic Calculator (基础计算器)

Implement a basic calculator to evaluate a simple expression string.
The expression string may contain open ( and closing parentheses ), the plus + or minus sign -, non-negative integers and empty spaces .
You may assume that the given expression is always valid.
Some examples:
"1 + 1" = 2
" 2-1 + 2 " = 3
"(1+(4+5+2)-3)+(6+8)" = 23

题目解析:

这一题只是混合了括号和加减号(+-),加入乘除法(*/)的话只是使优先级判断更复杂些,不影响整体算法思路。

四则运算的规则是:(1)先乘除,后加减;(2)从左算到右(3)先括号内,后括号外

为实现该算法,我们需要使用两个工作栈。一个称作OPT,用以寄存运算符;另一个称作NUM,用以寄存操作数或运算结果。其算法基本思路是,(1)首先置操作数栈和操作符栈为空栈;

(2)依次读入表达式中的每个字符,若是操作数则进NUM栈,若是运算符则和OPT栈的栈顶运算符比较优先权后作相应操作。

最难懂最关键的是第二步,1.若该运算符优先权大于栈顶运算符优先权则该运算符直接进OPT栈;反之若运算符优先权小于栈顶运算符的优先权,则弹出栈顶运算符,并从操作数NUM栈弹出两个数进行该栈顶运算符的运算,运算结果再加入NUM操作数栈。 2.循环往复地进行第1步判断。直到将该操作符加入OPT操作符栈

(3)表达式读取结束,若两个栈都不为空,则依次弹出OPT栈中的运算符和NUM栈中的两个操作数,进行运算后,将运算结果在加入NUM栈。直到OPT栈为空,NUM栈只剩下一个元素,则该元素就是运算结果。

(4)中间出现差错,比如最后NUM栈剩下不止一个数,则视为表达式出错

其中关键的第二步的关键是优先级的明确,基本就是按照四则运算的规则。这以复杂的问题就先不贴代码了,有时间再更新。

对于这一道题,解决方法略微简单,主要理解的是用栈的结构处理遇到括号时的办法。栈用来存储括号外的结果和符号,理解这一思想。

class Solution:
    def calculate(self, s):
        """
        :type s: str
        :rtype: int
        """
        stack = []
        l = len(s)
        i = 0
        sgn,res = 1, 0              # 初始化res和符号变量sgn 存储前一个符号
 
        while i <= l-1:             # 遍历s
            ch = s[i]
            if ch ==' ':
                i += 1
                
            elif ch.isdigit():      # 处理遇到的数字(只可处理整数,其他的自行修改)
                j = i
                while j <= l-1:
                    j += 1
                    if j==l or not s[j].isdigit():
                        break              
                num = int(s[i:j])   # 此时的操作数为num
                i = j
            elif ch =='+' or ch == '-':     # 遇到符号,对前面的结果进行计算
                res += sgn * num
                sgn = 1 if ch=='+' else -1  # 更新符号变量 sgn
                i += 1
                num = 0

            elif ch == '(':             # 左括号,将当前结果和符号 压入栈中
                stack.append(res)
                stack.append(sgn)
                sgn,res = 1, 0          # 重置res和sgn     
                i += 1
            elif ch == ')':
                res += sgn*num          # 遇到右括号,对前面的结果进行计算
                res *= stack.pop()      # 并取出栈中存储的res和sgn
                res += stack.pop()
                i += 1
                num = 0                 # 注意重置num=0 ,否则右括号后面第一个运算符会重复计算num

        if num!= 0:                     # 如果此时num不为0 继续计算
            res += sgn*num
        return res


下面这一题仍然与括号的匹配有关,括号互相嵌套,必然是递归的思想,也是栈的重要应用。

385. Mini Parser (迷你解析器)

Given a nested list of integers represented as a string, implement a parser to deserialize it.
Each element is either an integer, or a list -- whose elements may also be integers or other lists.
Note: You may assume that the string is well-formed:
String is non-empty.
String does not contain white spaces.
String contains only digits 0-9[- ,].
Example 1:
Given s = "324",You should return a NestedInteger object which contains a single integer 324.
Example 2:
Given s = "[123,[456,[789]]]",Return a NestedInteger object containing a nested list with 2 elements:
1. An integer containing value 123.
2. A nested list containing two elements:
    i.  An integer containing value 456.
    ii. A nested list with one element:
         a. An integer containing value 789.

题目解析:

此题开始读一两遍可能读不懂,事实上,我以为,题目叙述的也不严谨(或许是英语不好),理解之后就会明白本质上是一个括号匹配的问题,所以还是好好做一下这道题,把这一类问题理解了。

该题的答案大概两个版本,一是利用eval()函数,十分方便,二是挨个字符遍历。但是解决问题的本质是利用函数递归解析括号内的对象(括号内再有括号),利用了内存中函数调用栈,故我们见不到栈,但是算法思想是栈。

方法一:

def deserialize(self, s):
    def nestedInteger(x):
        if isinstance(x, int):
            return NestedInteger(x)
        lst = NestedInteger()
        for y in x:
            lst.add(nestedInteger(y))
        return lst
    return nestedInteger(eval(s))

方法二:

class Solution:
    def deserialize(self, s):
        """
        :type s: str
        :rtype: NestedInteger
        """

        def nestedInteger():
            num = ''
            while s[-1] in '1234567890-':
                num += s.pop()
            if num:
                return NestedInteger(int(num))
            s.pop()             # 此为左括号
            print('out',s)
            lst = NestedInteger()
            while s[-1] != ']':     # 遍历当前[]内的每一个元素(数字或list),遇到‘,’去掉
                lst.add(nestedInteger())
                print('loop',s)
                if s[-1] == ',':
                    s.pop()
            s.pop()             # 此为右括号
            return lst

还有一个一行代码的版本,要求我们熟悉匿名函数那一套理论,reduce函数以及 if-else语句,难懂==

def deserialize(self, s):
    return NestedInteger(s) if isinstance(s, int) else reduce(lambda a, x: a.add(self.deserialize(x)) or a, s, NestedInteger()) if isinstance(s, list) else self.deserialize(eval(s))
看这一篇博客可理解递归的实现和栈的关系,数据结构之---栈和递归&函数调用
方法一十分简洁易懂,通过eval()方法将字符串转换为列表对象,并遍历列表元素;方法二是遍历字符串,在这一方法中先将字符串s逆序,便于使用栈的pop方法,实际上还是自左向右遍历,先处理数字,若不是数字则是列表,思路与方法一相同,对应注释理解一下括号匹配的处理。方法二难懂可以自己过一遍,可以与方法一的步骤对比来看。


猜你喜欢

转载自blog.csdn.net/xutiantian1412/article/details/79958390