python演算法——字串表示式的計算(轉自:無限大地NLP_空木)
阿新 • • 發佈:2019-02-01
preface:最近有個面試,被要求給出一個字串表示式,計算出結果。本以為是見到過的,想著用一個棧,然後被面試官打斷說你這樣是有問題的,然後想了說用樹,又被打斷說是有問題的,再仔細想想。結果還是沒整出來。哎。回來翻了下leetcode發現有兩道類似。
但這兩道還是不夠。翻書加上與同仁的討論,發現只需要將表示式化為二叉樹的字尾表示式即可,有了字尾表示式,就比較簡單了。字尾表示式是能夠唯一確定表示式樹的。後序遍歷表示式樹即可。用棧對字尾表示式進行處理也可以的。主要還是需要將字串表示式化為字尾表示式。
另外,計算字串表示式,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 notin 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 notin 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):
- ifnot num1.isdigit() andnot 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()函式裡面做了些判斷。
主要參考: