关于后缀表达式的计算与中缀表达式转化为后缀表达式

关于后缀表达式

后缀表达式的计算方法是
遇到一个值就将这个值压入堆栈
遇到一个运算符就将两个值弹出 计算结果后在压入堆栈 从左向右进行扫描

12+34+*
的结果就是
(1+2)*(3+4) = 21

一般人所阅读的的表达式都是中缀表达式

1+2*4+3*(4*5+3)

中缀表达式中默认 同一个括号级别中的 */ 运算符比 +-运算符的优先级高
加了括号的话 括号里面的运算符的优先级比括号外面的运算符的优先级要高。

这样经一部抽象就可以把括号去掉抽象为以下的形式。
用Vi表示操作数i表示操作数的从左至右下标 用O(k)表示运算符。
k是运算符在整体的表达式中的优先级
所以举个例子:

1+2*4+3*(4*5+3) ->
# 只标明运算符优先级 运算符下面对应的 数字是优先级
1 + 2 * 4 + 3 * 4 * 5 + 3
  0   1   0   1   3   2

整个的表达式中 按照运算符优先级的从高到低 优先级最高的运算符两边的操作数
被按照操作符所指示的操作结合成一个值作为新的操作数放在对应的位置。然后一
步一步的结合 知道最后所有的操作符都消失此时只有一个数字作为结果

# step1
1 + 2 * 4 + 3 * 4 * 5 + 3
  0   1   0   1   3   2

# step2
1 + 2 * 4 + 3 * 20 + 3
  0   1   0   1    2

# step3
1 + 2 * 4 + 3 * 23
  0   1   0   1   

# step3
1 + 8 + 69
  0   0 

# step4
78

其实在计算表达式时 完全可以不必完全按照 元素安抚优先级从高到低的顺序来计算
例如 :

# 对于运算符优先级单调递增的表达式 计算的过程是从后向前结合
a * b + c + d + e * c
  0   1   2   3   4    
# 对于运算符优先级不是单调递增的表达式 如果从前向后结合的话
# 可以将前面一段单调递增的部分先算出来 再算后面各段单调递增的
a * b + c + d + e * f + g + h + i + j
  0   1   2   3   4   0   1   2   3  
# 可以先计算出 a~e 将结果压入堆栈再计算出 g~j 将结果压入堆栈
# 然后在求这两部分的和

对于后缀表达式 若中缀表达式的运算符优先级是单调递增的
那么

# midfix
a * b + c + d + e * c
  0   1   2   3   4 
# suffix
abcde*+++*
     43210

由上面可以看出 运算符左面优先级单调递增的表达式的 后缀表达式是操作数正序

例如:

1+2+3
 0 1

因为 后面的预案算符优先级更高 前面低优先级的运算要依赖后面高优先级的
运算的结果 所以 可以将低优先级的运算符和他左边的操作数都压到栈里面到
如果高优先级的运算符结合完了以后被输出到了输出流中 ,此时有了完整的有
操作数 堆栈中的低优先级操作符才能够输出。
将中缀表达式转换为后缀表达式的整个的这个过程有点类似于函数的嵌套调用
,外部的函数只有等到内部函数返回了以后才可以返回 外部函数就是类似于低
优先级的运算符,开始调用的地方相当于是将做操作数放入输出中,内部函数返
回的结果相当于是高优先级运算符的结果。函数的返回相当于将低优先级运算符
输出到输出中。
整个的过程中

{//0 begin
    {//1 begin
        此处不是最深的但是这个函数最先返回
    }//1 end
    {//1begin
        { //2begin
            // 最深的嵌套函数并不一定最先返回
        }//2 end
    }//1end
}//0 end

总结

归根结底`堆栈的使用都是包含 一个操作执行过程中需要的信息需要通过其他
类似的操作来获得的情况 这就需要使用堆栈来保存当前的信息 等到内部过程
结束后再从堆栈中弹出之前的执行信息 函数调用 后缀表达式的计算 以及中
缀表达式到后缀表达式的转换都符合这个规律

# coding=utf-8
# Author: SS4G
# Date 2017/03/15
# all test case passed


class SuffixExpression:
    """
    计算后缀表达式
    将中缀表达式转换为后缀表达式
    只处理 加减乘除 以及一位正整数 其他的暂时不管
    """
    def __init__(self):
        pass

    def calculate(self, operand0, operand1, opcode):
        """
        单纯的计算
        :param operand0:
        :param operand1:
        :param opcode:
        :return:
        """
        if opcode == "+":
            return operand0+operand1
        elif opcode == "-":
            return operand0-operand1
        elif opcode == "*":
            return operand0*operand1
        elif opcode == "/":
            return operand0/operand1

    def is_digits(self, s):
        return s in "0123456789"

    def is_opcode(self, s):
        return s in "+-*/"

    def calc_suffix(self, expression):
        """
        认为传入的后缀表达式是正确的
        calculate the suffix expression
        计算后缀表达式
        :param expression:
        :return:
        """
        op_num_stack = []
        for i in expression:
            if self.is_digits(i):
                op_num_stack.append(int(i))
            elif self.is_opcode(i):
                op1 = op_num_stack.pop()
                op0 = op_num_stack.pop()
                op_num_stack.append(self.calculate(op0, op1, i))
        return op_num_stack[0]

    def op_code_cmp(self, op0, op1):
        """
        比较操作符优先级
        :param op0:
        :param op1:
        :return: positive int if op0 > op1
                 zero  if op0 == op1
                 negtive int if op0 < op1
        """
        if (op0[1] > op1[1]) or (op0[1] == op1[1] and op0[2] > op1[2]):
            return 1
        if (op0[1] == op1[1]) and (op0[2] == op1[2]):
            return 0
        if (op0[1] < op1[1]) or (op0[1] == op1[1] and op0[2] < op1[2]):
            return -1

    def midfix2suffix(self, midfix_exp):
        """
        将中缀表达式转化为后缀表达式
        :param midfix_exp:
        :return:
        """
        base_rank = 0  # 当前的括号优先级
        # +- 优先级为1 */优先级为2
        # 实际的优先级为 基础优先级加上 括号优先级 嵌套的越深优先级越高
        opcode_stack = []
        opcode_array = []
        oprand_array = []
        output = []
        for i in midfix_exp:
            if i == "(":
                base_rank += 1
            elif i == ")":
                base_rank -= 1
            elif i == "+" or i == "-":
                opcode_array.append((i, base_rank, 0))
            elif i == "*" or i == "/":
                opcode_array.append((i, base_rank, 1))
            else:
                oprand_array.append(i)

        # print(opcode_array) # 查看操作符的优先级

        output.append(oprand_array[0])
        output.append(oprand_array[1])
        opcode_stack.append(opcode_array[0])
        i = 1  # 将要压入的操作符的索引 该索引对应的值尚未压入

        while (len(opcode_stack) > 0) or (i <= len(opcode_array)-1):
            # 操作符所处的位置不是在操作符的列表中就是在操作符的堆栈里
            # 所以只有这两个地方都没有操作符了才能说明所有的操作符都被输出到了输出流里面
            if i <= len(opcode_array)-1:  # 仍然有操作符在操作符列表中
                if len(opcode_stack) > 0:  # 如果当前堆栈中有之前的操作符 就要拿现在的操作符和之前的操作符去比较
                    # 如果之前的操作符的优先级更高就要保证之前的操作符先输出 然后才能将后面的操作数放入到输出流中
                    cmp_res = self.op_code_cmp(opcode_stack[-1], opcode_array[i])  # 将当前的运算符与后面的一个运算符比较优先级
                    if cmp_res >= 0:
                        output.append(opcode_stack.pop()[0])  # 将优先级大的操作符输出
                    else:  # 若当前的操作符更大那么将操作数放入输出中 操作符放入堆栈中 因为还不知道下一个操作符的优先级是否更高
                        opcode_stack.append(opcode_array[i])
                        output.append(oprand_array[i + 1])
                        i += 1
                else:
                    opcode_stack.append(opcode_array[i])
                    output.append(oprand_array[i+1])
                    i += 1
            else:  # 若所有操作符都被检索过了 只在操作符堆栈中还有操作符 那么就说明只可能在堆栈中还有操作符将他们按照次序弹出来就好
                output.append(opcode_stack.pop()[0])
        return "".join(output)

# Test

if __name__ == "__main__":
    s = SuffixExpression()
    testcases0 = [
        ("12*", 2),  # 1*2
        ("12*65+*", 22),  # 1*2*(6+5)
        ("123*+45*67+*+", 267),  # 1+2*3+4*5*(6+7)
    ]
    for testcase in testcases0:
        assert s.calc_suffix(testcase[0]) == testcase[1], "suffix calc error"

    testcases1 = [
        ("1+2*3+4-7*8-9", "123*+4+78*-9-"),
        ("1+2*(5*7+9)+2", "1257*9+*+2+"),
    ]

    for testcase in testcases1:
        try:
            assert s.midfix2suffix(testcase[0]) == testcase[1], "midfix to suffix error"
        except AssertionError:
            print("ERROR! ")
            print("return ", s.midfix2suffix(testcase[0]))
            print("require", testcase[1])

    testcase2 = [
        ("1+2*3+4+5*6+7+8+9", 65),
        ("1+2*(3+4)+(5*6)+(7+8+9)", 69),
        ("1+2*(3*4+5)*6+(7+8*9)", 284),
        ("(1+2)*3+(4+5)*(6+7)+8+9", 143),
        ("2+2", 4),
        ("3*3", 9),
        ("(((3+3)))", 6),
    ]
    for testcase in testcase2:
        try:
            assert s.calc_suffix(s.midfix2suffix(testcase[0])) == testcase[1], ""
        except AssertionError:
            print("ERROR!")
            print("require:", testcase[1])
            print("return :", s.calc_suffix(s.midfix2suffix(testcase[0])))
    print("END successfully!")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值