python算法——字符串表达式的计算

preface:最近有个面试,被要求给出一个字符串表达式,计算出结果。本以为是见到过的,想着用一个栈,然后被面试官打断说你这样是有问题的,然后想了说用树,又被打断说是有问题的,再仔细想想。结果还是没整出来。哎。回来翻了下leetcode发现有两道类似。

  1. leetcode 224 Basic Calculator https://leetcode.com/problems/basic-calculator/有点类似,可这特么只用加减和括号不用乘除啊,妈的智障。这题用栈AC了。
  2. leetcode 227 Basic Calculator II https://leetcode.com/problems/basic-calculator-ii/也有点类似,只是差了括号。

但这两道还是不够。翻书加上与同仁的讨论,发现只需要将表达式化为二叉树的后缀表达式即可,有了后缀表达式,就比较简单了。后缀表达式是能够唯一确定表达式树的。后序遍历表达式树即可。用栈对后缀表达式进行处理也可以的。主要还是需要将字符串表达式化为后缀表达式。

另外,计算字符串表达式,python有个内置函数eval(),直接使用eval(“1+2*(3-1)-4”)即可。

coding:

# -*- coding: utf-8 -*-
"""
Created on Sun Jul 10 15:39:28 2016

@author: Administrator
"""
operator_precedence = {
    '(' : 0,
    ')' : 0,
    '+' : 1,
    '-' : 1,
    '*' : 2,
    '/' : 2
}

def postfix_convert(exp):
    '''
    将表达式字符串,转为后缀表达式
    如exp = "1+2*(3-1)-4"
    转换为:postfix = ['1', '2', '3', '1', '-', '*', '+', '4', '-']
    '''
    stack = []          #运算符栈,存放运算符
    postfix = []        #后缀表达式栈
    for char in exp:
#        print char, stack, postfix
        if char not in operator_precedence:#非符号,直接进栈
            postfix.append(char)
        else:
            if len(stack) == 0:#若是运算符栈啥也没有,直接将运算符进栈
                stack.append(char)
            else:
                if char == "(":
                    stack.append(char)           
                elif char == ")":#遇到了右括号,运算符出栈到postfix中,并且将左括号出栈
                    while stack[-1]!="(":
                        postfix.append(stack.pop())
                    stack.pop()
                    
                elif operator_precedence[char] > operator_precedence[stack[-1]]:
                    #只要优先级数字大,那么就继续追加
                    stack.append(char)
                else:
                    while len(stack)!=0:
                        if stack[-1]=="(":#运算符栈一直出栈,直到遇到了左括号或者长度为0
                            break
                        postfix.append(stack.pop())#将运算符栈的运算符,依次出栈放到表达式栈里面
                    stack.append(char)#并且将当前符号追放到符号栈里面
                    
    while len(stack)!=0:#如果符号站里面还有元素,就直接将其出栈到表达式栈里面
        postfix.append(stack.pop())
    return postfix
#===========================这部分用于构造表达式树,不涉及计算================================#
class Node(object):
    def __init__(self, val):
        self.val = val
        self.left = None
        self.right = None
def create_expression_tree(postfix):
    """
    利用后缀表达式,构造二叉树
    """
    stack = []
    #print postfix
    for char in postfix:
        if char not in operator_precedence:
            #非操作符,即叶子节点
            node = Node(char)   
            stack.append(node)
        else:
            #遇到了运算符,出两个,进一个。
            node = Node(char)
            right = stack.pop()
            left = stack.pop()
            node.right = right
            node.left = left
            stack.append(node)
    #将最后一个出了即可。
    return stack.pop()
def inorder(tree):
    if tree:
        inorder(tree.left)
        print tree.val
        inorder(tree.right)
#=============================这部分用于计算值===================================#
def calculate(num1, op, num2):
    if not num1.isdigit() and not num2.isdigit():
        raise "num error"
    else:
        num1 = int(num1)
        num2 = int(num2)
    if op == "+":
        return num1 + num2
    elif op == "-":
        return num1 - num2
    elif op == "*":
        return num1 * num2
    elif op == "/":
        if num2==0:
            raise "zeros error"
        else:
            return num1/num2
    else:
        raise "op error"
    
def cal_expression_tree(postfix):
    stack = []
    for char in postfix:
        stack.append(char)
        if char in "+-*/":
            op    = stack.pop()
            num2  = stack.pop()
            num1  = stack.pop()
            value = calculate(num1, op, num2)
            value = str(value)#计算结果是数值类型,将其化为字符串类型
            stack.append(value)
    return int(stack[0])
if __name__=="__main__":
    #另外异常情况的判断的话,可以使用try catch
    exp = "1+2*(3-1)-4"
    postfix = postfix_convert(exp)
    
    #print "postfix:",postfix
    #tree    = create_expression_tree(postfix)
    #inorder(tree)
    
    print cal_expression_tree(postfix)

首先,运算符的优先级的定义operator_precedence字典的定义比较重要。遇到了(3-1*2)与(3*1-1)这两种情况,对三种运算符的优先级左右括号、减号、乘号,就不能简单的出栈进栈进行运算了。

其次,postfix_convert()函数将字符串表达式化为后缀表达式是关键。

最后,对于异常情况的处理,可以使用try catch,或者提前判断,这里在calculate()函数里面做了些判断。

主要参考:

  1. Expression tree
  2. 《数据结构与高分笔记》P132
  3. 顺序堆栈应用–表达式计算

转载请认证:无限大地nlp_空木的博客

    原文作者:无限大地NLP_空木
    原文地址: https://blog.csdn.net/u010454729/article/details/51872674
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞