棧是一種操作受限的線性表,只允許在一端插入或刪除數據,后進先出,先進后出,就是典型的棧結構。
棧主要包含2個操作,入棧和出棧,也就是在棧頂插入或刪除一個元素。
實現一個基於數組的順序棧:
class Stack:
def __init__(self, cap=64):
self.L = []
self.cap = cap
self.cnt = 0
def push(self, data):
if self.cnt == self.cap:
return False
self.L.append(data)
self.cnt += 1
return True
def pop(self):
if self.cnt == 0:
return None
v = self.L.pop()
self.cnt -= 1
return v
棧在表達式求值中的應用
將表達式簡化為只包含加、減、乘、除四則運算,例如:3 + 2 * 4 - 1,人腦可以迅速得出答案,但是對於計算機來說,理解這個表達式就是一件很難的事。
我們可以使用2個棧,一個保存數據,另一個保存操作符,從左向右遍歷表達式,當遇到數字就直接壓入數據棧,遇到操作符時就與操作符棧頂元素比較:
- 如果比棧頂操作符優先級高,直接將當前運算符入棧
- 如果比棧頂元素優先級低或者相同,則從操作符棧取出元素,再從數據棧取出2個元素進行計算,結果保存到數據棧,然后繼續比較
用代碼實現如下:
class UnsupportExpressionError(Exception):
pass
class UnsupportOperationError(Exception):
pass
class SimpleCalculator:
_priority = {
'+': 10, '-': 10,
'*': 20, '/': 20,
}
_digits = {
'0': 0, '1': 1, '2': 2, '3': 3, '4': 4,
'5': 5, '6': 6, '7': 7, '8': 8, '9': 9
}
def __init__(self, expression):
self.symbol_stack = Stack()
self.number_stack = Stack()
self.expression = expression
self._result = self.get_result()
@property
def result(self):
return self._result
def get_result(self):
decimal = 0
for x in self.expression:
v = x.strip()
if not v:
continue
if v in self._priority:
self.number_stack.push(decimal)
decimal = 0
self.cmp_and_calc(v)
self.symbol_stack.push(v)
elif v in self._digits:
decimal = decimal * 10 + self._digits[v]
else:
raise UnsupportExpressionError(f'unsupport operation: "{v}" in expression: {self.expression}')
self.number_stack.push(decimal)
operation = self.symbol_stack.pop()
while operation:
ret = self._calc_helper(operation)
self.number_stack.push(ret)
operation = self.symbol_stack.pop()
ret = self.number_stack.pop()
return ret
def cmp_and_calc(self, operation):
symbol = self.symbol_stack.pop()
if symbol is None:
return
if self._priority[operation] > self._priority[symbol]:
self.symbol_stack.push(symbol)
else:
ret = self._calc_helper(symbol)
self.number_stack.push(ret)
self.cmp_and_calc(operation)
def _calc_helper(self, operation):
b = self.number_stack.pop()
a = self.number_stack.pop()
if a is None:
return b
if operation == '+':
return a + b
elif operation == '-':
return a - b
elif operation == '*':
return a * b
elif operation == '/':
return a / b
else:
raise UnsupportOperationError(f'unsupport operation: {operation}')
def main():
s = ''
while not s or not s.strip():
s = input('please enter an expression: ')
c = SimpleCalculator(s)
print(c.result)
帶括號的表達式求值
同理可以使用一個棧保存表達式,當遇到右括號時,將該括號對里面的表達式使用上面的 SimpleCalculator 計算,得出的結果再壓入棧,最后得到的表達式就是一個不含括號的表達式了。
例如表達式:2*(6/(1+2)) 計算過程:
- 將 '2*(6/(1+2' 依次入棧
- 遇到 ')' ,不斷從棧中取出數據,直到有匹配的左括號
- 然后計算 '1+2' 將結果 3 入棧
- 遇到 ')' ,計算 '6/3' 將結果 2.0 入棧
- 最再計算表達式 2*2.0 得出最終結果: 4.0
SimpleCalculator 不支持小數,改寫 get_result 函數:
def get_result(self):
decimal = 0
has_dot = False
factor = 0
for x in self.expression:
v = x.strip()
if not v:
continue
if v == '.':
has_dot = True
factor = 0.1
elif v in self._priority:
self.number_stack.push(decimal)
decimal = 0
has_dot = False
factor = 0
self.cmp_and_calc(v)
self.symbol_stack.push(v)
elif v in self._digits:
if not has_dot:
decimal = decimal * 10 + self._digits[v]
else:
decimal = decimal + self._digits[v] * factor
factor = factor * 0.1
else:
raise UnsupportExpressionError(f'unsupport operation: "{v}" in expression: {self.expression}')
self.number_stack.push(decimal)
operation = self.symbol_stack.pop()
while operation:
ret = self._calc_helper(operation)
self.number_stack.push(ret)
operation = self.symbol_stack.pop()
ret = self.number_stack.pop()
return ret
帶括號的表達式求解:
class BracketsCalculator:
_match_pair = {
')': '(',
']': '[',
'}': '{',
}
def __init__(self, expression):
self.exp_stack = Stack()
self.expression = expression
self._result = self.get_result()
@property
def result(self):
return self._result
def get_result(self):
exp = ''
for x in self.expression:
v = x.strip()
if not v:
continue
if v in (')', ']', '}'):
val = self.exp_stack.pop()
while val != self._match_pair[v]:
exp = f'{val}{exp}'
val = self.exp_stack.pop()
ret = SimpleCalculator(exp).result
self.exp_stack.push(ret)
exp = ''
else:
self.exp_stack.push(v)
val = self.exp_stack.pop()
while val is not None:
exp = f'{val}{exp}'
val = self.exp_stack.pop()
return SimpleCalculator(exp).result
