自學一下數據結構,學完之后刷leetcode,使用python,從stack開始
Stack建立
class Stack:
def __init__(self):
self.items = []
def is_empty(self):
return self.items == []
def push(self,item):
self.items.append(item)
def pop(self):
return self.items.pop()
def peek(self):
return self.items[-1]
def size(self):
return len(self.items)
以上stack的top位置位於list的ending位置,如果要設置stack的top位置為list的beginning位置,只需要做如下改動
class Stack:
def __init__(self):
self.items = []
def is_empty(self):
return self.items == []
def push(self,item):
self.items.insert(0,item)
def pop(self):
return self.items.pop(0)
def peek(self):
return self.items[0]
def size(self):
return len(self.items)
這兩種方式的時間復雜度明顯不同,具體可以參見下表中的list處理的時間復雜度
pop(i)和insert(i,item)的時間復雜度為O(n),而pop()和append()的時間復雜度為O(1)
用stack在python中解決實際問題
括號對稱問題
這個問題貌似是個很經典的stack問題了,判斷string
中的括號是否對稱:
如:
()(()((())))
為對稱
(()()(()(())))
為不對稱
基本思路:
遍歷string中的每個字符
- 當遇到左括號
(
時,push到stack里 - 當遇到右括號
)
時,pop對應的左括號(
- 如果在遇到右括號
)
時,此時stack為空,則該右括號)
沒有對應的左括號,返回False
- 如果遍歷完所有字符后,stack不為空,則stack里全為左括號
(
,返回False
def par_checker(symbol_string):
s = Stack()
balanced = True
index =0
while index<len(symbol_string) and balanced:
symbol = symbol_string[index]
if symbol == '(':
s.push(symbol)
else:
if s.is_empty():
balanced = False
else:
s.pop()
index = index+1
if balanced and s.is_empty():
return True
else:
return False
print(par_checker('()((()))'))
print(par_checker('()(()'))
True
False
運用以上方法,可以解決類似問題,如括號為({[
的問題
將十進制數轉換為二進制數
很簡單,這里用的是高中的方法,除2取余,除2取余……
def divide_by_2(dec_number):
rem_stack = Stack()
while dec_number > 0:
rem = dec_number%2
rem_stack.push(rem)
dec_number = dec_number//2
# '//' 取整除,返回商整數部分
bin_string = ''
while not rem_stack.is_empty():
bin_string = bin_string + str(rem_stack.pop())
return bin_string
print(divide_by_2(42))
101010
這個方法可以自然的擴展到轉換為8進制(octal),16進制(hex),運用的也是同樣的方法,只是在代碼的開始建立一個’符號字典’。
def base_converter(dec_number, base):
digits = '0123456789ABCDEF'
rem_stack = Stack()
while dec_number>0:
rem = dec_number%base
rem_stack.push(rem)
dec_number = dec_number//base
new_string = ''
while not rem_stack.is_empty():
new_string = new_string + digits[rem_stack.pop()]
return new_string
print(base_converter(25, 8))
print(base_converter(30, 16))
31
1E
Infix, Prefix, Postfix相互轉換
1、轉換的作用:
Something very important has hap- pened. Where did the parentheses go? Why don’t we need them in prefix and postfix? The answer is that the operators are no longer ambiguous with respect to the operands that they work on. Only infix notation requires the additional symbols. The order of operations within prefix and postfix expressions is completely determined by the position of the operator and nothing else. In many ways, this makes infix the least desirable notation to use.
2、轉換結果:
簡單來說,這一切都跟運算符的先后順序有關。乘除法的優先率要高於加減法,如果要改變這種定律,只能加括號。而perfix和postfix的表示方法給了一種用加括號的方式來改變這種運算順序。
Prefix:將運算符置於運算數之前,如
+AB
表示A+B
;
+A*BC
表示A+B*C
;
若要表示(?+?)*?
,只需寫*+ABC
(先執行+AB
,后執行*(+AB)C
)
3、轉換方法(postfix):
- 創建一個stack來儲存運算符,創建一個list來做輸出
- 將infix表達式轉換為list from string
- 從左往右遍歷2中的list,假設每一遍歷的元素為token:
- 如果token是運算數,直接輸出
- 如果token是左括號
(
,push到stack里 - 如果token是右括號
)
,將stack里的元素逐個pop出來並輸出直到遇到對應的左括號(
- 如果token是運算符,push到stack里。然而,在push之前,首先按順序pop出stack里比該運算符優先級相同或者更高的運算符,並輸出。
def infix_to_postfix(infix_expr):
# 建立一個字典用來表示運算符的優先級:其中左括號的優先級最低
prec = {}
prec["*"] =3
prec["/"] =3
prec["+"] =2
prec["-"] =2
prec["("] =1
op_stack = Stack()
postfix_list=[] # output
token_list = infix_expr.split()
for token in token_list:
# 如果token是運算數,直接輸出
if token in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" or token in "0123456789":
postfix_list.append(token)
# 如果token是左括號(,push到stack里
elif token == '(':
op_stack.push(token)
# 如果token是右括號),將stack里的元素逐個pop出來並輸出直到遇到對應的左括號(
elif token == ')':
top_token = op_stack.pop()
while top_token != '(':
postfix_list.append(top_token)
top_token = op_stack.pop()
else:
# 如果token是運算符,push到stack里。然而,在push之前,首先按順序pop出stack里比該運算符優先級相同或者更高的運算符,並輸出。
while (not op_stack.is_empty()) and (prec[op_stack.peek()]>=prec[token]):
postfix_list.append(op_stack.pop())
op_stack.push(token)
while not op_stack.is_empty():
postfix_list.append(op_stack.pop())
# str.join() 方法用於將序列中的元素以指定的字符連接生成一個新的字符串
return ' '.join(postfix_list)
print(infix_to_postfix("A * B + C * D"))
print(infix_to_postfix("( A + B ) * C - ( D - E ) * ( F + G )"))
A B * C D * +
A B + C * D E - F G + * -
Postfix使用
當轉換成postfix格式后,使用確實是一個問題,但下圖已經很好的詮釋了這一過程。
- 創建一個stack用來儲存運算數
- 將string轉換成list
- 從左往右遍歷上述list,for evevy token in list:
- 如果token是一個運算數,將其轉換為interger並push到stack中
- 如果token是一個運算符,它需要兩個運算數來進行計算。pop兩次,第一次pop的數在左,第二次pop的數在右,並用該運算符進行計算,將計算后的值push到stack里
- 當list中的值處理完畢,最后的結果在stack中
def postfix_eval(postfix_expr):
operand_stack = Stack()
token_list = postfix_expr.split()
for token in token_list:
if token in "0123456789":
operand_stack.push(int(token))
else:
operand2 = operand_stack.pop()
operand1 = operand_stack.pop()
result = do_math(token, operand1, operand2)
operand_stack.push(result)
return operand_stack.pop()
def do_math(op, op1, op2):
if op == "*":
return op1 * op2
elif op == "/":
return op1 / op2
elif op == "+":
return op1 + op2
else:
return op1 - op2
print(postfix_eval('7 8 + 3 2 + /'))