Python 简单计算器-逆波兰后缀表达式实现

中缀表达式
9 + (3 - 1)*3 + 8 / 2
后缀表达式
9 3 1 - 3 * + 8 2 / +
简易计算器,可以通过栈来实现。然而如果直接使用中缀表达式,需要处理括号,而使用后缀表达式则可以规避这种麻烦。后缀表达式计算起来更加方便,步骤如下:

1.将后缀表达式入栈,数字直接入栈

2.遇到操作符,将栈顶的两个元素出栈,
第一个出栈的是操作数,第二个出栈的是被操作数,
根据操作符计算两个数的结果,然后将结果再次入栈

3.重复上面的步骤,直到后缀表达式遍历结束,最后在栈中的元素便是计算结果

例:9 3 1 - 3 * + 8 2 / +
第一步:
  后缀表达式的第一个元素是9,数字直接入栈。
第二步:
  后缀表达式的第二个元素3,数字直接入栈。
第三步:
  后缀表达式的第三个元素1,数字直接入栈。
第四步:
  后缀表达式的第四个元素‘-’,将栈顶的两个元素出栈,
  第一个出栈的1作为操作数,第二个出栈的3作为被操作数,
  而操作符是‘-’,计算结果为:3 - 1 = 2,然后将2入栈。
第五步:
  后缀表达式的第五个元素3,数字直接入栈。
第六步:
  后缀表达式的第六个元素‘*’,将栈顶的两个元素出栈,
  第一个出栈的3作为操作数,第二个出栈的2作为被操作数,
  而操作符是‘-’,计算结果为:2 * 3 = 6,然后将6入栈。
第七步:
  后缀表达式的第七个元素‘+’,将栈顶的两个元素出栈,
  第一个出栈的6作为操作数,第二个出栈的9作为被操作数,
  而操作符是‘-’,计算结果为:9 + 6 = 15,然后将15入栈。
第八步:
   后缀表达式的第八个元素8,数字直接入栈。
第九步:
   后缀表达式的第九个元素2,数字直接入栈。
第十步:
  后缀表达式的第十个元素‘/’,将栈顶的两个元素出栈,
  第一个出栈的2作为操作数,第二个出栈的8作为被操作数,
  而操作符是‘-’,计算结果为:8 / 2 = 4,然后将4入栈。
第十一步:
  后缀表达式的第十一个元素‘/’,将栈顶的两个元素出栈,
  第一个出栈的4作为操作数,第二个出栈的15作为被操作数,
  而操作符是‘-’,计算结果为:15 + 4 = 19,然后将19入栈。
第十二步:
  后缀表达式遍历结束,栈中的元素就是最后的计算结果。
在了解了后缀表达式的计算过程,我们可以通过栈轻松实现该计算过程,那如何将中缀表达式转化成后缀表达式?

中缀表达式转化为后缀表达式

规则:从左到右遍历中缀表达式的数字和符号,若是数字就输出,即成为后缀表达式的一部分;若是符号,则判断其与栈顶符号的优先级。是右括号或优先级低于栈顶符号(乘除优先加减)则栈顶元素依次出栈并输出,并将当前符号进栈,一直到最终输出后缀表达式为止。

具体实现步骤:中缀表达式转化为后缀表达式可以通过栈和队列来实现,
需要一个栈,用来存操作符,需要一个队列,用来存后缀表达式的结果。

1.遍历中缀表达式,如果是数字则直接入后缀表达式队列

2.如果操作符栈为空,操作符直接入操作符栈

3.如果操作符的优先级大于操作符栈顶元素的优先级,则直接入栈,
否则栈顶元素出栈,并添加到后缀表达式队列中,直到栈顶元素小于操作符的优先级,然后将该操作符入栈到操作符栈

4.如果操作符是右括号,操作符做出栈操作,且添加到后缀表达式队列中,
直到左括号出栈,左括号不需添加到后缀表达式队列中

5.中缀表达式遍历结束,需将操作符栈的元素依次出栈,并添加到后缀表达式队列中

例:9 + (3 - 1)*3 + 8 / 2
第一步:
  中缀表达式的第一个元素是9,数字直接入后缀表达式队列
  结果:
    中缀表达式队列:9
    操作符栈:null
第二步:
  中缀表达式的第二个元素是‘+’,操作符栈为空,符号‘+’直接入栈
  结果:
    中缀表达式队列:9
    操作符栈:+
第三步:
  中缀表达式的第三个元素是‘(’,括号的优先级大于操作符栈顶元素,‘(’直接入栈
  结果:
    中缀表达式队列:9
    操作符栈:+ (
第四步:
  中缀表达式的第四个元素是3,数字直接入后缀表达式队列
  结果:
    中缀表达式队列:9 3
    操作符栈:+ (
第五步:
  中缀表达式的第五个元素是‘-’,
  由于栈顶元素是‘(’操作符,‘(’操作符只有遇到‘)’操作符才会出栈,
  故‘-’直接入栈
  结果:
    中缀表达式队列:9 3
    操作符栈:+ ( -
第六步:
  中缀表达式的第六个元素是1,数字直接入后缀表达式队列
  结果:
    中缀表达式队列:9 3 1
    操作符栈:+ ( -
第七步:
  中缀表达式的第七个元素是‘)’,遇到右括号,栈顶元素依次出栈,
  并入到后缀表达式队列中,直到‘(’出栈,‘(’出栈后不做处理
  结果:
    中缀表达式队列:9 3 1 -
    操作符栈:+
第八步:
  中缀表达式的第八个元素是‘*’,优先级高于操作符栈栈顶元素‘+’的优先级,故直接入栈
  结果:
    中缀表达式队列:9 3 1 -
    操作符栈:+ *
第九步:
  中缀表达式的第九个元素是3,数字直接入后缀表达式队列
  结果:
    中缀表达式队列:9 3 1 - 3
    操作符栈:+ *
第十步:
  中缀表达式的第十个元素是‘+’,优先级不大于操作符栈栈顶元素,
  栈顶元素出栈,并入到后缀表达式队列中,
  直到栈顶元素的优先级小于该元素的优先级,最后该元素入栈到操作符栈
  结果:
    中缀表达式队列:9 3 1 - 3 * +
    操作符栈:+ 
第十一步:
  中缀表达式的第十一个元素是8,数字直接入后缀表达式队列
  结果:
    中缀表达式队列:9 3 1 - 3 * + 8
    操作符栈:+ 
第十二步:
  中缀表达式的第十二个元素是‘/’,优先级高于操作符栈栈顶元素的优先级,故直接入栈
  结果:
    中缀表达式队列:9 3 1 - 3 * + 8
    操作符栈:+ /
第十三步:
  中缀表达式的第十三个元素是2,数字直接入后缀表达式队列
  结果:
    中缀表达式队列:9 3 1 - 3 * + 8 2
    操作符栈:+ /
第十四步:
  中缀表达式遍历结束,将操作符栈栈顶元素依次出栈,并入到后缀表达式的队列中
  结果:
    中缀表达式队列:9 3 1 - 3 * + 8 2 / +
    操作符栈:null

代码

class Calculator(object):

    def __init__(self):
        # 操作符集合
        self.operators = ['+', '-', '*', '/', '(', ')']
        # 操作符优先级
        self.priority = {
            '+': 1,
            '-': 1,
            '*': 2,
            '/': 2,
            '(': 3,
            ')': 3
        }

    def generate_postfix_expression(self, expression):
        """
        生成后缀表达式
        :param expression:
        :return:
        """

        # 去除表达式中所有空格
        expression = expression.replace(' ', '')

        # 创建list作为栈,list的append和pop方法刚好和栈的入栈和出栈方法相同
        # 操作符栈
        operator_stack = list()
        # 后缀表达式是从尾部插入数据,使用后缀表达式计算结果是从头开始遍历,可以理解为先进先出,可以使用队列实现,
        # 这里为了简便用list代替,不做内存释放处理
        expression_stack = list()

        for element in expression:
            # 如果是数字则直接入表达式栈
            if element in self.operators:

                # 如果栈为空,操作符直接入操作符栈,或者为左括号,也直接入操作符栈
                if not operator_stack:
                    operator_stack.append(element)
                else:
                    # 如果目标元素是右括号,操作符栈顶出栈直接遇到左括号,且出栈的操作符除了括号入到表达式队列中
                    if element == ')':
                        for top in operator_stack[::-1]:
                            if top != '(':
                                expression_stack.append(top)
                                operator_stack.pop()
                            else:
                                operator_stack.pop()
                                break
                    else:
                        for top in operator_stack[::-1]:
                            # 如果目标元素大于栈顶元素,则直接入栈,否则栈顶元素出栈,入到表达式队列中
                            # 左括号只有遇到右括号才出栈
                            if self.priority[top] >= self.priority[element] and top != '(':
                                expression_stack.append(top)
                                operator_stack.pop()
                            else:
                                operator_stack.append(element)
                                break

                        # 可能操作符栈所有的元素优先级都大于等于目标操作符的优先级,这样的话操作符全部出栈了,
                        # 而目标操作符需要入栈操作
                        if not operator_stack:
                            operator_stack.append(element)

            else:
                expression_stack.append(element)

        # 中缀表达式遍历结束,操作符栈仍有操作符,将操作符栈中的操作符入到表达式栈中
        for i in range(len(operator_stack)):
            expression_stack.append(operator_stack.pop())

        return expression_stack

    def calcaulate(self, expression):
        # 生成后缀表达式
        expression_result = self.generate_postfix_expression(expression)
        # 使用list作为栈来计算
        calcalate_stack = list()

        # 遍历后缀表达式
        for element in expression_result:
            # 如果为数字直接入栈
            # 遇到操作符,将栈顶的两个元素出栈
            if element not in self.operators:
                calcalate_stack.append(element)
            else:
                # 操作数
                number1 = calcalate_stack.pop()
                # 被操作数
                number2 = calcalate_stack.pop()
                # 结果 =  被操作数 操作符 操作数 (例:2 - 1)
                result = self.operate(number1, number2, element)
                # 计算结果入栈
                calcalate_stack.append(result)

        return calcalate_stack[0]

    def operate(self, number1, number2, operator):
        """
        计算结果
        :param number1: 操作数
        :param number2: 被操作数
        :param operator: 操作符
        :return: 
        """
        number1 = int(number1)
        number2 = int(number2)

        if operator == '+':
            return number2 + number1
        if operator == '-':
            return number2 - number1
        if operator == '*':
            return number2 * number1
        if operator == '/':
            return number2 / number1


if __name__ == '__main__':
    c = Calculator()
    expression = '9 + (3 - 1)*3 + 8 / 2'
    expression_result = c.calcaulate(expression)
    print(expression_result)

代码还有许多小问题,还需要优化,比如时间复杂度还要降低,中缀表达式没法正确拆分,数字是十位以上的就会出错了,十位个位等会被拆成单个字符。

你可能感兴趣的:(Python 简单计算器-逆波兰后缀表达式实现)