递归和队列实现基本计算器

题目:

实现一个基本的计算器来计算一个简单的字符串表达式的值。
字符串表达式仅包含非负整数,+, - ,*,/ 四种运算符和空格 。 整数除法仅保留整数部分。
LeetCode:https://leetcode-cn.com/problems/basic-calculator-ii/
思路
先不考虑空格,以及乘除、括号,假设现在有一个最简单的表达式,只有加减两种运算符,也没有括号,应该如何运算?

  • 显而易见,从左到右,依次遍历即可,那我们先把这样的情况翻译成代码,借助队列,队列中从0位置到最后一个位置,对应表达式中从左到右每个字符的位置(假设一个字符就是一个数值,先不考虑字符拼数字),代码如下所示:
def get_num(arr):
    res = 0
    tmp = True

    while len(arr) != 0:
        cur = arr.popleft()
        if cur == '+':
            tmp = True
        elif cur == '-':
            tmp = False
        else:
            res += int(cur) if tmp else -int(cur)

    return res

现在,再来考虑如何将一个表达式转变成队列,首先考虑到乘除的计算优先级问题,也就是说当我们从左到右将字符压入队列时,要优先保证乘除先被计算,所以当我们遇到加减运算符时,可以直接入队列,但如果遇到了乘除法,要先将乘除法计算完毕后再将结果入队列,这样就避免了后续计算顺序的问题。
新的问题又来了,如何在遇到乘除时,先计算呢?

  • 解决策略是,入队列时,不管当前是什么运算符,暂且先压入队列,当遇到下一个数字要压入队列时,再判断这个数字之前的运算符是什么,如果是乘除,就弹出运算符和运算符之前的数字,先计算,将计算结果入队列。
def add_new(stack, num):
    if len(stack) != 0:
        if stack[-1] == '-' or stack[-1] == '+':
            stack.append(num)
        elif stack[-1] == '*':
            stack.pop()
            new = int(stack.pop()) * int(num)
            stack.append(str(int(new)))
        else:
            stack.pop()
            new = int(stack.pop()) / int(num)
            stack.append(str(int(new)))
    else:
        stack.append(num)

    return stack

其中,num是要新入队列的数。有一点要注意,当队列为空时,不需要判断计算,直接压入即可;

乘除的问题解决了,现在考虑括号的问题,因为括号里的表达式同样也是一个完整的计算过程,并且是独立于括号外面的,所以利用递归解决。
所以,总的思路为:

  1. 从头开始遍历字符串中每个位置的字符,如果遇到的字符是数字,收集起来;
  2. 如果遇到的字符是左括号,进入递归过程;
  3. 如果遇到了运算符,说明当前收集到的数字字符已经构成一个完整的计算数值了,将收集到的数字压入队列,再将运算符压入队列,同时之前收集的数字字符需要归零;
  4. 如果遇到了空格,则结束当前循环,进行下一位置判断;
  5. 整个过程的结束条件是,已经到达字符串末尾了或者遇到了右括号,此时需要将队列中表达式计算并返回结果;
    整个代码如下:
def process(string, i):
    num = '0'
    stack = collections.deque()
    while (i < len(string) and string[i] != ')'):

        if string[i] == ' ':  
            i += 1
            continue
        if string[i] >= '0' and string[i] <= '9': # 遇到数字了,收集
            num += string[i]
            i += 1
        elif string[i] == '(': # 遇到左括号,要进入递归
            num, i = process(string, i + 1)
            stack = add_new(stack, num) # 可有可无,因为左括号之前一定是某个运算符,此时num一定是0,加入不影响计算
            i += 1
        else: # 遇到运算符
            stack = add_new(stack, num)
            stack.append(string[i])
            num = '0'
            i += 1
    add_new(stack, num) # 此时,还需要将字符串中最后一个数字或者右括号前面的数字压入队列

    return calc(stack), i

def add_new(stack, num):
    if len(stack) != 0:
        if stack[-1] == '-' or stack[-1] == '+':
            stack.append(num)
        elif stack[-1] == '*':
            top = stack.pop()
            new = int(stack.pop()) * int(num)
            stack.append(str(int(new)))
        else:
            top = stack.pop()
            new = int(stack.pop()) / int(num)
            stack.append(str(int(new)))
    else:
        stack.append(num)

    return stack

def calc(stack):
    res = 0
    tmp = True
    while len(stack) != 0:
        cur = stack.popleft()
        if cur == '-':
            tmp = False
        elif cur == '+':
            tmp = True
        else:
            res += int(cur) if tmp else -int(cur)

    return res
    
def main():
    res, _ = process(string, 0)
    return res

猜你喜欢

转载自blog.csdn.net/yaogepila/article/details/106456782