- 正在學習的算法課程:極客時間的王爭老師的《數據結構與算法之美》
- 傳送門: https://time.geekbang.org/column/126
- 代碼傳送門:https://github.com/Lebhoryi/Algorithms/tree/master/3.%E6%A0%88%E5%92%8C%E9%98%9F%E5%88%97
- 目前學到第八講,很良心,共56講
- 2019/10/07 - 2019/10/17
- 棧一般用順序表實現,隊列一般用鏈表實現
一、棧
1. 定義
棧(stack)是限定僅在表尾進行插入和刪除操作的線性表。
- 何時選擇棧?
當某個數據集合只涉及在一段插入和刪除數據,並且滿足後進先出、先進後出的特性,首選棧
2. 棧實現
棧的主要操作有兩個:
-
入棧:棧頂插入一個數據
-
出棧:棧頂刪除一個數據
根據實現方式不同,分爲:
-
順序棧:用數組實現的棧
-
鏈式棧:用鏈表實現的棧
時間複雜度和空間複雜度均是O(1)
3. 棧的應用
最常見的是函數調用棧
int main() {
int a = 1;
int ret = 0;
int res = 0;
ret = add(3, 5);
res = a + ret;
printf("%d", res);
reuturn 0;
}
int add(int x, int y) {
int sum = 0;
sum = x + y;
return sum;
}
4. 遞歸
- 遞歸的祕訣:
將大問題分解爲小問題,寫出遞推公式和終止條件。
- 警惕:
-
不要用人腦去分解遞歸的每個調用關係,即每一個步驟
-
不要棧溢出
-
不要重複計算
5. 代碼實現棧的操作123
-
創建空棧
-
push(入棧)
-
pop(出棧)
-
top(取出棧最後壓入的元素)
- 順序棧,python用表實現,表尾爲棧頂:
# coding=utf-8
'''
@ Summary: python中的list可以滿足棧的操作,但是多了棧所沒有的操作
缺乏安全性,因此把list作爲類的內部,作爲實現基礎
@ Update:
@ file: 3-1.棧的順序表實現.py
@ version: 1.0.0
@ Author: [email protected]
@ Date: 19-10-7 下午6:17
'''
class SStack(object):
def __init__(self):
self._elem = []
def is_empty(self):
# 是否空棧
return self._elem == []
def top(self):
# 取棧頂元素
if self.is_empty():
return None
return self._elem[-1]
def push(self, elem):
# 入棧
return self._elem.append(elem)
def pop(self):
# 出棧
if self.is_empty():
return None
return self._elem.pop()
if __name__ == "__main__":
st1 = SStack()
st1.push(3)
# print(st1.top())
st1.push(5)
while not st1.is_empty():
print(st1.pop())
- 鏈接表實現棧,表頭爲棧頂:
# coding=utf-8
'''
@ Summary: 用鏈表實現棧的相關操作
@ Update:
@ file: 3-2.棧的鏈接表實現.py
@ version: 1.0.0
@ Author: [email protected]
@ Date: 19-10-7 下午6:29
'''
class Node(object):
def __init__(self, val):
self.val = val
self.next = None
def __repr__(self):
return str(self.val)
class LStack(object):
@ staticmethod
def c_link(vals):
# create link
if not vals:
return None
head = Node(len(vals)) # head node
move = head # move node
for val in vals:
tmp = Node(val)
move.next = tmp
move = tmp
return head.next
@ staticmethod
def p_link(link):
# print link
if not link:
return None
while link:
yield link.val
link = link.next
@ staticmethod
def top(link):
# 取出棧頂元素
if not link:
return None
return link.val
@ staticmethod
def push(link, elem):
# 入棧
elem = Node(elem)
elem.next = link
return elem
@ staticmethod
def pop(link):
# 出棧
if not link:
return None
p = link
link = link.next
return p.val, link
if __name__ == "__main__":
_list = list(range(1, 7))
# link = None
# for i in _list:
# link = LStack.push(link, i)
# for node in LStack.p_link(link):
# print(node, end=" ")
top = LStack.c_link(_list)
for node in LStack.p_link(top):
print(node, end=" ")
print()
# output: 1 2 3 4 5 6
top = LStack.push(top, 10)
for node in LStack.p_link(top):
print(node, end=" ")
print()
# output: 10 1 2 3 4 5 6
elem, top = LStack.pop(top)
print(elem)
for node in LStack.p_link(top):
print(node, end=" ")
print()
elem = LStack.top(top)
print(elem)
- 代碼練習1:二進制轉十進制
# coding=utf-8
'''
@ Summary: 二進制轉十進制,用棧實現
@ Update:
@ file: 3-3.二進制轉十進制.py
@ version: 1.0.0
@ Author: [email protected]
@ Date: 19-10-7 下午7:16
'''
class SStack(object):
def __init__(self):
self._elem = []
def is_empty(self):
# 是否空棧
return self._elem == []
def push(self, elem):
# 入棧
return self._elem.append(elem)
def pop(self):
# 出棧
if self.is_empty():
return None
return self._elem.pop()
if __name__ == "__main__":
_input = 1010110
_list = list(map(int, str(_input)))
st1 = SStack()
for i in _list:
st1.push(i)
print(st1._elem)
sum = count = 0
while not st1.is_empty():
# print(st1.pop(), end=" ")
_pop = st1.pop()
sum = sum + _pop * 2**count
count += 1
print(sum)
- 代碼練習2:二進制轉八進制
# coding=utf-8
'''
@ Summary: 用棧實現二進制轉八進制
@ Update:
@ file: 3-3.二進制轉八進制.py
@ version: 1.0.0
@ Author: [email protected]
@ Date: 19-10-7 下午10:05
'''
class SStack(object):
def __init__(self):
self._elem = []
def is_empty(self):
# 是否空棧
return self._elem == []
def push(self, elem):
# 入棧
return self._elem.append(elem)
def pop(self):
# 出棧
if self.is_empty():
return None
return self._elem.pop()
if __name__ == "__main__":
_input = 11001001 # 初始值
_list = list(map(int, str(_input))) # int to list
while len(_list) % 3 != 0: # 三的倍數,不夠補0
_list.insert(0, 0)
_list.insert(0, 0) # 爲了後面的while 循環到棧底的時候繼續運行,增加一個0
st1 = SStack() # 二進制的棧
st2 = SStack() # 八進制的棧
for i in _list: # 二進制digui入棧
st1.push(i)
print(st1._elem)
# 二進制 三個彈出,計算,壓入八進制棧
tmp = [] # 儲存三個二進制數
count = 1 # 計數
while not st1.is_empty():
if count < 4:
# 找到三個二進制數
tmp.append(str(st1.pop()))
count += 1
else:
num = "".join(tmp)
# 二進制轉八進制
num = int(num[2]) * 2**2 + int(num[1]) * 2**1 + \
int(num[0]) * 2**0
st2.push(num) # 入棧
count = 1
tmp = []
print(st2._elem)
- 代碼練習3:逆波蘭表達式
# coding=utf-8
'''
@ Summary: 逆波蘭表達式,又稱後綴表達式
@ Update:
@ file: 3-4.逆波蘭表達式.py
@ version: 1.0.0
@ Author: [email protected]
@ Date: 19-10-8 下午3:29
'''
class Node(object):
def __init__(self, val):
self.val = val
self.next = None
def __repr__(self):
return str(self.val)
class LStack(object):
def __init__(self):
self.top = None
def push(self, elem):
# 入棧
elem = Node(elem)
elem.next = self.top
self.top = elem
def pop(self):
# 出棧
if not self.top:
return None
p = self.top
self.top = self.top.next
return p.val
def p_link(self):
if not self.top:
return None
while self.top:
print(self.top.val, end=" ")
self.top = self.top.next
def RPN(_list):
# _int = [str(i) for i in range(20)]
operators = "+-*/"
st1 = LStack()
for item in _list:
try:
item = int(item)
st1.push(item)
except ValueError:
if item in operators:
x2 = st1.pop()
x1 = st1.pop()
if item == "+":
st1.push(x1 + x2)
elif item == "-":
st1.push(x1 - x2)
elif item == "*":
st1.push(x1 * x2)
elif item == "/":
st1.push(x1 / x2)
return st1.top
if __name__ == "__main__":
# _input = "1 2 - 4 5 + *" # -9
# _input = "5 6 7 + 8 * - 9 4 / +" # -96.75
_input = "3 5 - 6 17 4 * + * 3 /"
_list = _input.split(" ") # str to list
print(RPN(_list))
---
- 代碼練習4: 中綴表達式轉後綴表達式
```python
# coding=utf-8
'''
@ Summary: 中綴表達式轉換爲後綴表達式
@ Update:
@ file: 3-6.逆波蘭進階.py
@ version: 1.0.0
@ Author: [email protected]
@ Date: 19-10-8 下午4:43
'''
class Node(object):
def __init__(self, val):
self.val = val
self.next = None
def __repr__(self):
return str(self.val)
class LStack(object):
def __init__(self):
# 初始化 top node
self._top = None
def push(self, elem):
# 入棧 self._top 始終指向first node
elem = Node(elem)
elem.next = self._top
self._top = elem
def pop(self):
# 出棧 self._pop指向第二個節點, 返回第一個節點的值
if not self._top:
return None
p = self._top
self._top = self._top.next
return p.val
def mid_2_after(l):
# 中綴轉後綴
# 優先級
ops_relu = {"+":1, "-":1,
"*":2, "/":2,
"(":1, ")":1}
stack = LStack() # 儲存運算符和"()"的棧
lt = [] # 輸出列表
for item in l:
# 遇到數字添加進列表中,非數字進行棧的相關操作
try:
item = int(item)
lt.append(item)
except ValueError:
if item != ")":
# 優先級順序,棧頂的優先級高則彈出棧中所有元素
if stack._top and ops_relu[item] < ops_relu[stack._top.val]:
while stack._top:
lt.append(stack.pop())
stack.push(item) # 入棧
else:
# 遇到")", 彈出棧中的元素, 直到"("爲止
while stack._top.val != "(":
lt.append(stack.pop())
stack.pop() # delete "("
# 彈出棧中所有元素
while stack._top:
lt.append(stack.pop())
return lt
if __name__ == "__main__":
_input = "1 + ( 2 - 3 ) * 4 + 10 / 5" # "1 2 3 - 4 * + 10 5 / +"
_list = _input.split(" ") # str to list
print("The origin list is: {}.".format(_list))
print(mid_2_after(_list))
- 代碼練習4:斐波那契
最常見的棧:函數調用
# coding=utf-8
'''
@ Summary: 斐波那契
@ Update:
@ file: 3-7.斐波那契.py
@ version: 1.0.0
@ Author: [email protected]
@ Date: 19-10-8 下午10:18
'''
def fib(n):
# 遞歸
if n < 2:
return n
return fib(n-1) + fib(n-2)
def fib2(n):
# 生成器
if n < 0:
return None
a, b = 0, 1
for _ in range(n):
yield b
a, b = b, b + a
if __name__ == "__main__":
for i in fib2(5):
print(i)
- 代碼練習5:字符串反轉
# coding=utf-8
'''
@ Summary: 遞歸實現字符串翻轉
@ Update:
@ file: 3-8.字符串翻轉.py
@ version: 1.0.0
@ Author: [email protected]
@ Date: 19-10-9 上午9:05
'''
def reverse(s):
if len(s) <= 1:
return s
elif len(s) == 2:
return s[1] + s[0]
return s[-1] + reverse(s[:-1])
if __name__ == "__main__":
s = "ABC"
print(reverse(s))
- 代碼練習6:漢諾塔
# coding=utf-8
'''
@ Summary: 遞歸實現漢諾塔
@ Update:
@ file: 3-9.漢諾塔.py
@ version: 1.0.0
@ Author: [email protected]
@ Date: 19-10-9 上午9:19
'''
def hanoi(n, x, y, z):
if n == 1:
print(x + " --> " + z)
else:
# 將n-1個從X移動到Y上,藉助Z
hanoi(n-1, x, z, y)
# 將第n個從X移動到Z上
print(x + " --> " + z)
# 將n-1個從Y移動到Z上,藉助X
hanoi(n-1, y, x, z)
if __name__ == "__main__":
hanoi(4, "X", "Y", "Z")
- 代碼練習7:八皇后問題
- 代碼練習8:括號匹配
# coding=utf-8
'''
@ Summary:
@ Update:
@ file: 括號匹配.py
@ version: 1.0.0
@ Author: [email protected]
@ Date: 10/15/19 9:55 PM
'''
def is_valid(s):
if not s:
return None
parent = "()[]{}"
opposite = {")":"(", "]":"[", "}":"{"}
st = [] # 棧
for item in s:
if item in parent:
st.append(item)
if item in opposite:
del_elem = st.pop()
if not st or opposite[del_elem] != st.pop():
return False
return not st
if __name__ == "__main__":
s = "9(){}"
if is_valid(s):
print("{} is OK.".format(s))
else:
print("{} is not OK.".format(s))
二、隊列
1. 定義
先進先出,比如排隊買票
2. 入隊、出隊
- 鏈式隊列(鏈表表頭爲隊頭,表尾爲隊尾)
# coding=utf-8
'''
@ Summary: 鏈表實現隊列,任意輸入一串字符 # 結束
@ Update:
@ file: 隊列.py
@ version: 1.0.0
@ Author: [email protected]
@ Date: 10/17/19 4:21 PM
'''
class Node(object):
def __init__(self, val):
self.val = val
self.next = None
def __repr__(self):
return str(self.val)
class LQueue(object):
def __init__(self):
self.front = Node(0)
self.rear = Node(0)
def is_empty(self):
# 判斷是否爲空
return self.front.val == self.rear.val
def enqueue(self, elem):
# 入隊
elem = Node(elem)
if self.is_empty():
self.front.next = elem
self.rear.next = elem
self.rear = elem
def dequeue(self):
# 出隊
if self.is_empty():
return None
del_node = self.front.next.val
if self.front.next == self.rear:
# 當只有一個元素的時候
self.front = self.rear
else:
self.front.next = self.front.next.next
return del_node
def get_head(self):
# 查看隊頭元素
if self.is_empty():
return None
return self.front.next
def p_queue(self):
# print queuep
if self.is_empty():
return None
tmp = self.front.next
while tmp:
print(tmp, end=" ")
tmp = tmp.next
if __name__ == "__main__":
# 創建隊列
lq = LQueue()
data = input("請輸入(#結束):")
while data != "#":
lq.enqueue(data)
data = input("請輸入(#結束):")
lq.p_queue() # 最初的鏈式列表
- 順序隊列的出隊和入隊
# coding=utf-8
'''
@ Summary: 順序隊列
@ Update:
@ file: 隊列2.py
@ version: 1.0.0
@ Author: [email protected]
@ Date: 10/18/19 12:19 AM
'''
class SQueue(object):
def __init__(self):
self._queue = []
def is_empty(self):
return self._queue == []
def en_queue(self, elem):
self._queue.insert(0, elem)
# self._queue.append(elem)
def de_queue(self):
if self.is_empty():
return None
del_node = self._queue.pop()
# del_node = self._queue.pop(0)
return del_node
def p_queue(self):
if self.is_empty():
return None
return self._queue
if __name__ == "__main__":
sq = SQueue()
data = input("請輸入(輸入# 結束):")
while data != "#":
sq.en_queue(data)
data = input("請輸入(輸入# 結束):")
print(sq.p_queue())
sq.de_queue()
print(sq.p_queue())
隊列跟棧一樣,也是一種操作受限的線性表數據結構。
3. 循環隊列
# coding=utf-8
'''
@ Summary: 順序表實現循環隊列
@ Update:
@ file: 循環隊列.py
@ version: 1.0.0
@ Author: [email protected]
@ Date: 10/18/19 10:24 AM
'''
class SQueue(object):
def __init__(self, maxsize):
self.maxsize = maxsize # 自定義隊列長度
self._queue = [None] * maxsize
self.front = 0 # 指向隊頭
self.rear = 0 # 指向隊尾元素的下一個位置
def is_empty(self):
# 判斷是否爲空
return self.front == self.rear
def q_length(self):
# 當前隊列長度計算 (rear-front+maxsize)%maxsize
return (self.rear - self.front + self.maxsize) % self.maxsize
def enqueue(self, elem):
# 隊尾入隊 elem push
if (self.rear + 1) % self.maxsize == self.front:
# 判斷隊列是否填滿 (rear+1)%maxsize == front
print("The queue is full!")
self._queue[self.rear] = elem
self.rear = (self.rear + 1) % self.maxsize
def dequeue(self):
# 隊頭出隊 elem pull
if self.is_empty():
return None
del_elem = self._queue[self.front]
self._queue[self.front] = None
self.front = (self.front + 1) % self.maxsize
return del_elem
def p_queue(self):
# print queue
if self.is_empty():
return None
for i in range(self.maxsize):
print(self._queue[i], end=" ")
print()
if __name__ == "__main__":
s = "abcdde"
sq = SQueue(15) # 循環隊列
for item in range(10):
# 入隊
sq.enqueue(item)
sq.p_queue() # 原始隊列
for i in range(5): # 刪除隊頭的5個元素:0~4
sq.dequeue()
sq.p_queue()
for i in range(8): # 從隊尾增加8個元素:0~7
sq.enqueue(i)
sq.p_queue()
4.代碼練習:
- 用兩個棧實現隊列 (劍指offer和力扣)
# coding=utf-8
'''
@ Summary: 劍指Offer-用兩個棧來實現一個隊列,完成隊列的Push和Pop操作
@ Update: (先進先出)
@ file: 3-15.2棧實現隊列.py
@ version: 1.0.0
@ Author: [email protected]
@ Date: 19-10-21 下午10:00
'''
import pysnooper
class SStack(object):
def __init__(self):
self.stackA = []
self.stackB = []
def is_empty(self):
# 判斷satackA是否爲空
return self.stackA == []
def push(self, elem):
# stackA入棧
self.stackA.append(elem)
@pysnooper.snoop(watch=("self.stackA", "self.stackB"))
def s_pop(self):
# stackA 出棧 stackB 入棧
if self.stackB:
return self.stackB.pop()
while self.stackA:
self.stackB.append(self.stackA.pop())
return self.stackB.pop()
if __name__ == "__main__":
ss = SStack()
for i in range(7):
ss.push(i)
print(ss.s_pop())
ss.push(8)
print(ss.s_pop())
- 用兩個隊列實現棧(力扣)
# coding=utf-8
'''
@ Summary: 進棧:元素入隊列A(後進先出)
出棧:判斷如果隊列A只有一個元素,則直接出隊。否則,把隊A中的元素出隊併入隊B,
直到隊A中只有一個元素,再直接出隊。爲了下一次繼續操作,互換隊A和隊B。
@ Update:
@ file: 3-16.2隊列實現棧.py
@ version: 1.0.0
@ Author: [email protected]
@ Date: 19-10-22 下午7:12
'''
class SQueue(object):
def __init__(self):
self.queueA = []
self.queueB = []
def is_empty(self):
return self.queueA == [] and self.queueB == []
def enqueue(self, elem):
# 入隊
self.queueA.append(elem)
def dequeue(self):
#出隊
if self.is_empty():
return None
# if len(self.queueA) == 1:
# return self.queueA.pop()
while len(self.queueA) > 1:
self.queueB.append(self.queueA.pop(0))
self.queueA, self.queueB = self.queueB, self.queueA
return self.queueB.pop()
if __name__ == "__main__":
sq = SQueue()
for i in range(1, 7):
sq.enqueue(i)
print(sq.dequeue())
sq.enqueue(3)
print(sq.dequeue())
print(sq.dequeue())