这道题的考察用法应该是用堆栈,因为python3没有栈,所以用list仿造一个堆栈。

堆栈类 class Stack

class Stack:
    def __init__(self):
        self.items = []
    def push(self, item):
        self.items.append(item)
    def pop(self):
        return self.items.pop()
    def peek(self):
        return self.items[-1]
    def size(self):
        return len(self.items)

分割器

本题首先给出一个字符串,所以要定义一个splitter,分割数字和字符。

这里我用的是一个双指针的办法,pre是前一个字符,如果是数字字符,就用0,如果是别的运算字符,就用1。

当发现从数字变成运算符的时候,lst增加整个数字。

当发现从运算符变成数字的时候,lst分别增加每一个运算符。

每次增加,再把该项设为空字符。

def splitter(string):
    lst = []
    pre = -1        # 0 means num, 1 means char
    now = -1
    num = ''
    char = ''
    for ii in string:
        if ii.isdigit():
            num += ii
            now = 0
        else:
            char += ii
            now = 1
        if now == 1 and pre == 0:
            lst.append(int(num))
            num = ''
        elif now == 0 and pre == 1:
            lst += list(char)
            char = ''
        pre = now
    if now == 0:
        lst.append(int(num))
    else:
        lst += list(char)
    return lst

简单运算

先定义一个简单运算器,处理不含括号的运算式,遵循先乘除,后加减,从左到右的原则。

定义两个栈,

首先要处理负数,在简单运算中,只有第一个数字可能是负数(标准的表达式)。 所以,如果第一个运算符是取负运算符,先执行之,再入栈。如果不是,直接入栈。

然后执行乘法和除法,因为store栈是正向入栈的,出栈的时候是从表达式的右边向左边,这会出错。所以先把store栈堆入calc栈中。再从calc栈进行从左到右的运算。每次遇到乘号或者除号就从两个栈各取一个运算,然后入栈store。这样即使是连续乘除也不会错。

然后执行加减法,逻辑和乘除一样。

def simple_calculator(lst):
    stack_store = Stack()
    stack_calc = Stack()
    
    # Negative
    if lst[0] == '-':
        stack_store.push(-lst[1])
        for ii in lst[2:]:
            stack_store.push(ii)
    else:
        for ii in lst:
            stack_store.push(ii)

    # Multiply & Divide
    while stack_store.size() > 0:
        stack_calc.push(stack_store.pop()) # 先把栈堆入calc栈当中
    while stack_calc.size() > 0:
        element = stack_calc.pop()
        if element == '*':
            res = stack_store.pop() * stack_calc.pop() # 遇到乘号和除号就运算,然后放入store当中
            stack_store.push(res)
        elif element == '/':
            res = stack_store.pop() / stack_calc.pop()
            stack_store.push(res)
        else:
            stack_store.push(element)

    # addition & subtraction
    while stack_store.size() > 0:
        stack_calc.push(stack_store.pop()) # 加减也是同理的
    while stack_calc.size() > 0:
        element = stack_calc.pop()
        if element == '+':
            res = stack_store.pop() + stack_calc.pop()
            stack_store.push(res)
        elif element == '-':
            res = stack_store.pop() - stack_calc.pop()
            stack_store.push(res)
        else:
            stack_store.push(element)
            
    return stack_store.peek() # 此时store栈中应该只有一个元素,那就是结果了。

计算器(带括号)

处理完了不带括号的,再处理带括号的。

同样的,使用栈进行括号处理。

在前面,我们首先已经获得了一个不带括号的简单运算器,返回值是一个数字(注意,可以是浮点数。但是输入值必须是整型,其实稍微改一改splitter也可以处理浮点运算)

同样的,生成两个栈。从store栈往calc栈移动,如果遇到了左括号,则从calc栈开始出栈元素,放入空列表lst2中。当遇到右括号时,停止出栈,把lst2放入简单计算器中计算结果。

随后,把结果入栈calc。

因为是遇到第一个左括号就开始从calc出栈,直到遇到右括号为止,这样保证了是从最里面的括号开始计算。

运算完之后,又继续出入栈,直到整个队列不含括号。并且全部入栈calc。

随后,calc出栈,再进行一次简单运算,就可以得到结果。

def calculator(lst):
    stack_store = Stack()
    stack_calc = Stack()
    for ii in lst:
        stack_store.push(ii) # 先把列表放入栈中。
    while stack_store.size() > 0:
        element = stack_store.pop() # 从store栈移动向calc栈,遇到左括号执行行为。
        if element == '(':
            lst2 = []
            ele2 = stack_calc.pop() # 遇到左括号后,从cal栈出栈一个list。这里是不含括号的,执行简单运算。
            while ele2 != ')':
                lst2.append(ele2)
                ele2 = stack_calc.pop()
            stack_calc.push(simple_calculator(lst2)) # 把简单运算的结果放入calc栈中,同时两个括号正好已经被取出,无视之。
        else:
            stack_calc.push(element) # 没遇到左括号就直接放入calc
    lst3 = []
    for ii in range(stack_calc.size()): # 最后,剩下一个不含括号的表达式,再次执行。得到最终结果
        lst3.append(stack_calc.pop())
    return simple_calculator(lst3)

主程序

while True:
    try:
        lst = splitter(input())
        print(calculator(lst))
    except:
		break

总结

用堆栈是比较好处理一些需要在字符串中来回的问题的。比如说括号,需要找到最里面的一个,再反向找另一个,用两个堆栈比较合适。

对于从左到右的运算,其实是队列,当然,两个字符串也就是队列了。

对于python来说,用list加上指针,利用切片的办法,完全可以做出一样的行为。

不管是从左到右,还是回头计算。只不过这道题要求了栈,才特地构建了这个类。

当然,人生苦短,如果是面试,我们需要认真的讲解自己的想法。如果是机考,建议直接eval!!