preface:最近有个面试,被要求给出一个字符串表达式,计算出结果。本以为是见到过的,想着用一个栈,然后被面试官打断说你这样是有问题的,然后想了说用树,又被打断说是有问题的,再仔细想想。结果还是没整出来。哎。回来翻了下leetcode发现有两道类似。
- leetcode 224 Basic Calculator https://leetcode.com/problems/basic-calculator/有点类似,可这特么只用加减和括号不用乘除啊,妈的智障。这题用栈AC了。
- 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()函数里面做了些判断。
主要参考:
- Expression tree
- 《数据结构与高分笔记》P132
- 顺序堆栈应用–表达式计算
转载请认证:无限大地nlp_空木的博客