題目
給你一個字串表示式 s ,請你實現一個基本計算器來計算並返回它的值。
輸入:s = "1 + 1"
輸出:2
輸入:s = " 2-1 + 2 "
輸出:3
輸入:s = "(1+(4+5+2)-3)+(6+8)"
輸出:23
s 由數字、'+'、'-'、'('、')'、和 ' ' 組成
s 表示一個有效的表示式
'+' 不能用作一元運算(例如, "+1" 和 "+(2 + 3)" 無效)
'-' 可以用作一元運算(即 "-1" 和 "-(2 + 3)" 是有效的)
輸入中不存在兩個連續的操作符
每個數字和執行的計算將適合於一個有符號的 32位 整數
思路
計算器類的題目,224、227、772,只需要一個思路:逆波蘭表示式+棧。
a+b,逆波蘭表示式:a,b,+
例如:s = '2+(35/4+7(2+3))/4'
定義兩個棧:
stack棧 用來存放數字以外的符號:運算子、括號
res棧 用來存放數字,以及pop出來的運算子,形成逆波蘭表示式
給出一個加減乘除全都有的思路:
思路規則:
數字,放入res
符號,放入stack
噹噹前符號的優先順序,<=stack棧頂的符號優先順序時,需要將stack棧頂的符號pop出去,放入res中,再將當前符號放入stack中
當遇到 )時,將stack中最後一個( 之後的所有運算子都pop出去,放入res中
構建逆波蘭表示式的過程:
2+(3*5/4+7*(2+3))/4
當前字元 2,壓入res
stack: []
res: ['2']
當前字元 +,壓入stack
stack: ['+']
res: ['2']
當前字元是 (,壓入stack
stack: ['+', '(']
res: ['2']
當前字元 3,壓入res
stack: ['+', '(']
res: ['2', '3']
當前字元 *,壓入stack
stack: ['+', '(', '*']
res: ['2', '3']
當前字元 5,壓入res
stack: ['+', '(', '*']
res: ['2', '3', '5']
當前字元 /,優先順序<=stack棧頂的*, 將stack棧頂的* pop到res中,再將當前的/壓入stack
stack: ['+', '(', '/']
res: ['2', '3', '5', '*']
當前字元 4,壓入res
stack: ['+', '(', '/']
res: ['2', '3', '5', '*', '4']
當前字元 +,優先順序<=stack棧頂的/, 將stack棧頂的/ pop到res中,再將當前的+壓入stack
stack: ['+', '(', '+']
res: ['2', '3', '5', '*', '4', '/']
當前字元 7,壓入res
stack: ['+', '(', '+']
res: ['2', '3', '5', '*', '4', '/', '7']
當前字元 *,壓入stack
stack: ['+', '(', '+', '*']
res: ['2', '3', '5', '*', '4', '/', '7']
當前字元是 (,壓入stack
stack: ['+', '(', '+', '*', '(']
res: ['2', '3', '5', '*', '4', '/', '7']
當前字元 2,壓入res
stack: ['+', '(', '+', '*', '(']
res: ['2', '3', '5', '*', '4', '/', '7', '2']
當前字元 +,壓入stack
stack: ['+', '(', '+', '*', '(', '+']
res: ['2', '3', '5', '*', '4', '/', '7', '2']
當前字元 3,壓入res
stack: ['+', '(', '+', '*', '(', '+']
res: ['2', '3', '5', '*', '4', '/', '7', '2', '3']
當前字元 ),將stack中最後一個 ( 之後的運算子都pop到res中
stack: ['+', '(', '+', '*']
res: ['2', '3', '5', '*', '4', '/', '7', '2', '3', '+']
當前字元 ),將stack中最後一個 ( 之後的運算子都pop到res中
stack: ['+']
res: ['2', '3', '5', '*', '4', '/', '7', '2', '3', '+', '*', '+']
當前字元 /,壓入stack
stack: ['+', '/']
res: ['2', '3', '5', '*', '4', '/', '7', '2', '3', '+', '*', '+']
當前字元 4,壓入res
stack: ['+', '/']
res: ['2', '3', '5', '*', '4', '/', '7', '2', '3', '+', '*', '+', '4']
stack中剩餘的操作符,後進先出的pop到res中
stack: []
res: ['2', '3', '5', '*', '4', '/', '7', '2', '3', '+', '*', '+', '4', '/', '+']
逆波蘭表示式計算過程:
字元: 2
入棧: [2]
字元: 3
入棧: [2, 3]
字元: 5
入棧: [2, 3, 5]
字元: *
取出棧頂後兩位進行相應的運算,結果入棧: [2, 15]
字元: 4
入棧: [2, 15, 4]
字元: /
取出棧頂後兩位進行相應的運算,結果入棧: [2, 3]
字元: 7
入棧: [2, 3, 7]
字元: 2
入棧: [2, 3, 7, 2]
字元: 3
入棧: [2, 3, 7, 2, 3]
字元: +
取出棧頂後兩位進行相應的運算,結果入棧: [2, 3, 7, 5]
字元: *
取出棧頂後兩位進行相應的運算,結果入棧: [2, 3, 35]
字元: +
取出棧頂後兩位進行相應的運算,結果入棧: [2, 38]
字元: 4
入棧: [2, 38, 4]
字元: /
取出棧頂後兩位進行相應的運算,結果入棧: [2, 9]
字元: +
取出棧頂後兩位進行相應的運算,結果入棧: [11]
python程式碼:
def calculate(s: str) -> int:
if s.strip().isdigit():
return int(s)
n = len(s)
stack = [] # 用來存運算子和括號的棧
res = [] # 用來存數字的棧
dic = { # 操作符優先順序字典
'+':1,
'-':1,
'*':2,
'/':2,
'%':2,
'^':3
}
for i in range(n):
# 列印日誌
underline = '\033[4m'
end = '\033[0m'
print(s[0:i]+underline + s[i] + end+s[i+1:])
if s[i]==' ':
continue
# 1. 遇到數字,直接壓入res棧
if s[i].isdigit():
# ‘11’這種,前一位也是數字的,需要進行處理
if i!=0 and s[i-1].isdigit():
print('當前字元 %s,前一個字元也是數字 %s,特殊處理'%(s[i],s[i-1]))
res.append(str(int(res.pop())*10+int(s[i])))
else:
print('當前字元 %s,壓入res'%(s[i]))
res.append(s[i])
else:
# 2. 遇到右括號,將stack棧頂到最近的左括號之間的運算子,後進先出的pop到res中
if s[i]==')':
print('當前字元 %s,將stack中最後一個 ( 之後的運算子都pop到res中'%(s[i]))
while True:
if stack[-1]=='(':
stack.pop()
break
else:
res.append(stack.pop())
# 3. 遇到左括號,直接壓入stack
elif s[i]=='(':
print('當前字元是 (,壓入stack')
stack.append('(')
# 4. '-2+1' 和 '1-(-2+1)'這兩邊界情況,將-2當作0-2處理,在res中也壓入0
elif s[i]=='-' and (i==0 or s[i-1]=='(') :
print('當前字元是 -,前面是(或著沒有字元,當作0-處理,res中壓入0,stack壓入-')
res.append('0')
stack.append('-')
else:
print('當前字元 %s'%s[i],end=',')
# 5. 遇到運算子,壓入stack棧
# 如果當前運算子優先順序 <= stack棧頂運算子優先順序,
# 則將stack棧定的運算子pop到res棧中,再壓入當前運算子到stack
if len(stack) and stack[-1] in dic and dic[s[i]]<=dic[stack[-1]]:
print('優先順序<=stack棧頂的%s, 將stack棧頂的%s pop到res中,再將當前的%s壓入stack'%(stack[-1],stack[-1],s[i]))
res.append(stack.pop())
stack.append(s[i])
else:
print('壓入stack')
# 否則直接壓入stack棧中
stack.append(s[i])
print('stack:',stack)
print('res:',res)
print('\n')
# 最後將stack中剩餘的操作符,後進先出的pop到res中
for _ in range(len(stack)):
res.append(stack.pop())
# print('stack中剩餘的操作符,後進先出的pop到res中')
# print('stack:',stack)
# print('res:',res)
# res就是最終的逆波蘭表示式,開始計算res的值
# 再新建一個空棧_res,遍歷逆波蘭表示式,將數字壓入到_res中
# 當遍歷到運算子時,_res棧中最後兩個數字就是參與該運算的數字
# 注意:要注意數字的順序,a,b,/ => a/b, a,b,^= a**b
_res = []
for ch in res:
# print('字元:',ch)
if ch.isdigit():
_res.append(int(ch))
# print('入棧:',_res)
else:
b = _res.pop()
a = _res.pop()
if ch=='+':
_res.append(a+b)
elif ch=='-':
_res.append(a-b)
elif ch=='*':
_res.append(a*b)
elif ch=='/':
_res.append(a//b)
elif ch=='%':
_res.append(a%b)
elif ch=='^':
_res.append(a**b)
# print('取出棧頂後兩位進行相應的運算,結果入棧:',_res)
# print('\n')
return _res
無print清爽版:
def calculate(s: str) -> int:
if s.strip().isdigit():
return int(s)
n = len(s)
stack = []
res = []
dic = {
'+':1,
'-':1,
'*':2,
'/':2,
'%':2,
'^':3
}
for i in range(n):
if s[i]==' ':
continue
if s[i].isdigit():
if i!=0 and s[i-1].isdigit():
res.append(str(int(res.pop())*10+int(s[i])))
else:
res.append(s[i])
else:
if s[i]==')':
while True:
if stack[-1]=='(':
stack.pop()
break
else:
res.append(stack.pop())
elif s[i]=='(':
stack.append('(')
elif s[i]=='-' and (i==0 or s[i-1]=='(') :
res.append('0')
stack.append('-')
else:
if len(stack) and stack[-1] in dic and dic[s[i]]<=dic[stack[-1]]:
res.append(stack.pop())
stack.append(s[i])
else:
stack.append(s[i])
for _ in range(len(stack)):
res.append(stack.pop())
_res = []
for ch in res:
if ch.isdigit():
_res.append(int(ch))
else:
b = _res.pop()
a = _res.pop()
if ch=='+':
_res.append(a+b)
elif ch=='-':
_res.append(a-b)
elif ch=='*':
_res.append(a*b)
elif ch=='/':
_res.append(a//b)
elif ch=='%':
_res.append(a%b)
elif ch=='^':
_res.append(a**b)
return _res
邊界情況總結:
- '123456', ' 123456',s直接就是一串數字字串,程式碼開頭s.strip().isdigit()判斷一下直接返回即可。
- '-2+1',1-(-2)',在字串開頭或者(後,接一個符號,按0-2處理
- '1+111',多位數的判斷,res.append(str(int(res.pop())*10+int(s[i])))