LEETCOCE 224. Basic Calculator
Implement a basic calculator to evaluate a simple expression string.
The expression string may contain open (
and closing parentheses )
, the plus +
or minus sign -
, non-negative integers and empty spaces
.
意思是实现只有加减法的带空格的算术表达式。
1.用构造二叉树的方法
对于一个算式,找到最后一个被使用的运算符作为划分,以此运算符为界,递归计算左边的值,递归计算右边的值,然后以此运算符进行运算,即可得到结果.最后一个被使用的运算符是什么呢? 例如: 2+3*(4-1)-5/1 肯定先算括号里的, 然后算* / 法,最后才考虑+,-法 . 所以,先考虑+,-法,再考虑* / 法. 括号外的+-*/可能很多,所以确定一个原则,找整个算式中最右边+-,*/. 由于要找的是括号外的+-*/, 所以得想办法避免记录括号内的+-*/,所以设置了一个标志p,初始0 一旦遇到一个左括号, p+1, 这时候说明目前在括号内,不应该记录+-*/, 当遇到右括号,p-1,p恢复为0,这时候说明目前已经走出括号,可以记录+-*/ . 扫描完真个算时候,C1记录了最右边的括号外的+-号,C2记录了最右边的括号外的*/号. 如果c1<0,说明没扫描到括号外的+-号, 那么只能考虑*/号作为最后一个运算的运算符了. 把c1=c2,然后判断c1<0, 如果还<0, 说明括号外也没有*/号, 说明整个算式被括号包围起来了. 所以可以递归运算时忽略这对括号,即递归(x+1,y-1)的算式, 返回它的子树根.代码如下:
class Treenode:
def __init__(self,x):
self.val=x
self.left=self.right=None
def calculate(s):
"""
:type s: str
:rtype: int
"""
def buildTree(s):
n=len(s)
if n==1:return Treenode(s[0])
k,p=-1,0
for i in range(n):
c=s[i]
if c=='(':
p+=1
elif c==')':
p-=1
elif c in ('+','-'):
if p==0:k=i
if k<0:return buildTree(s[1:-1])
root=Treenode(s[k])
root.left=buildTree(s[:k])
root.right=buildTree(s[k+1:])
return root
def f(root):
if root.left==None:return int(root.val)
l=f(root.left)
r=f(root.right)
if root.val=='+':return l+r
return l-r
t,i=[],0
while i<len(s):
c=s[i]
if c in '()+-':
t.append(c)
i+=1
elif c==' ':
i+=1
else:
k=i
while i<len(s) and s[i] in '0123456789':i+=1
t.append(s[k:i])
#print(t)
root=buildTree(t)
return f(root)
上述算法可以做个优化,不用真的构造二叉树再遍历,二个过程合二为一.代码如下:
def calculate(s):
"""
:type s: str
:rtype: int
"""
def dp(s):
n=len(s)
if n==1:return int(s[0])
p=0
for i in range(n-1,-1,-1):
c=s[i]
if c=='(':
p+=1
elif c==')':
p-=1
elif c in ('+','-'):
if p==0:break
else:return dp(s[1:-1])
l=dp(s[:i])
r=dp(s[i+1:])
if s[i]=='+':return l+r
return l-r
t,i=[],0
while i<len(s):
c=s[i]
if c in '()+-':
t.append(c)
i+=1
elif c==' ':
i+=1
else:
k=i
while i<len(s) and s[i] in '0123456789':i+=1
t.append(s[k:i])
#print(t)
return dp(t)
2.逆波兰表达式
逆波兰表达式又称后缀表达式,运算符位于操作数之后.比如(3+4)×5-6的逆波兰表达式是“3 4 + 5 × 6 -”.生成逆波兰表达式的算法如下:从左至右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(次顶元素 op 栈顶元素),并将结果入栈;重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果.具体点:
- 初始化两个栈:运算符栈s1和储存中间结果的栈s2;
- 从左至右扫描中缀表达式;
- 遇到操作数时,将其压s2;
- 遇到运算符时,比较其与s1栈顶运算符的优先级:
- 如果s1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
- 否则,若优先级比栈顶运算符的高,也将运算符压入s1;
- 否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4-1)与s1中新的栈顶运算符相比较;
- 遇到括号时:
- 如果是左括号“(”,则直接压入s1;
- 如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃;
- 重复步骤2至5,直到表达式的最右边;
- 将s1中剩余的运算符依次弹出并压入s2;
- 依次弹出s2中的元素并输出,结果即为后缀表达式
def calculate(s):
"""
:type s: str
:rtype: int
"""
s1,s2,n,i=[],[],len(s),0
while i<n:
c=s[i]
if c in '+-':
while s1 and s1[-1]!='(':s2.append(s1.pop())
s1.append(c)
i+=1
elif c=='(':
s1.append(c)
i+=1
elif c==')':
op=s1.pop()
while op!='(':
s2.append(op)
op=s1.pop()
i+=1
elif c==' ':
i+=1
else:
k=i
while i<len(s) and s[i] in '0123456789':i+=1
s2.append(s[k:i])
while s1:s2.append(s1.pop())
stack=[]
for c in s2:
if c in '+-':
r,l=stack.pop(),stack.pop()
if c=='+':
stack.append(l+r)
else:
stack.append(l-r)
else:
stack.append(int(c))
return stack[-1]
3.其他方法
def calculate(s):
"""
:type s: str
:rtype: int
"""
sign,val,curr,stack = 1,0,0,[]
for c in s:
if c in '0123456789':
curr = curr * 10 + int(c)
elif c in '+-':
val += sign * curr
curr,sign = 0,int(c+'1')
elif c == '(':
stack.append(val)
stack.append(sign)
sign,val = 1,0
elif c == ')':
val += sign * curr
curr = 0
val *= stack.pop()
val += stack.pop()
if curr: val += curr * sign
return val