佇列(queue),是先進先出(FIFO, First-In-First-Out)的線性表,在具體應用中通常用連結串列或者陣列來實現,佇列只允許在後端(稱為rear)進行插入操作,在前端(稱為front)進行刪除操作,佇列的操作方式和堆疊類似,唯一的區別在於佇列只允許新資料在後端進行新增。
摘錄維基百科
如圖所示
佇列的介面
一個佇列至少需要如下介面:
介面 | 描述 |
---|---|
add(x) | 入隊 |
delete() | 出隊 |
clear() | 清空佇列 |
isEmpty() | 判斷佇列是否為空 |
isFull() | 判斷佇列是否未滿 |
length() | 佇列的當前長度 |
capability() | 佇列的容量 |
然而在Python中,可以使用collections
模組下的deque
函式,deque
函式提供了佇列所有的介面,那麼先讓我門看看佇列deque
函式提供了那些API把:
collections.deque是雙端佇列,即左右兩邊都是可進可出的
方法 | 描述 |
---|---|
append(x) | 在佇列的右邊新增一個元素 |
appendleft(x) | 在佇列的左邊新增一個元素 |
clear() | 從佇列中刪除所有元素 |
copy() | 返回一個淺拷貝的副本 |
count(value) | 返回值在佇列中出現的次數 |
extend([x..]) | 使用可迭代的元素擴充套件佇列的右側 |
extendleft([x..]) | 使用可迭代的元素擴充套件佇列的右側 |
index(value, [start, [stop]]) | 返回值的第一個索引,如果值不存在,則引發ValueError。 |
insert(index, object) | 在索引之前插入物件 |
maxlen | 獲取佇列的最大長度 |
pop() | 刪除並返回最右側的元素 |
popleft() | 刪除並返回最左側的元素 |
remove(value) | 刪除查詢到的第一個值 |
reverse() | 佇列中的所有元素進行翻轉 |
rotate() | 向右旋轉佇列n步(預設n = 1),如果n為負,向左旋轉。 |
現在我們在Python中測試下這些個API的使用吧。
入隊操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
>>> from collections import deque # 建立一個佇列 >>> q = deque([1]) >>> q deque([1]) # 往佇列中新增一個元素 >>> q.append(2) >>> q deque([1, 2]) # 往佇列最左邊新增一個元素 >>> q.appendleft(3) >>> q deque([3, 1, 2]) # 同時入隊多個元素 >>> q.extend([4,5,6]) >>> q deque([3, 1, 2, 4, 5, 6]) # 在最左邊同時入隊多個元素 >>> q.extendleft([7,8,9]) >>> q deque([9, 8, 7, 3, 1, 2, 4, 5, 6]) |
出隊操作
1 2 3 4 5 6 7 8 9 10 |
# 刪除佇列中最後一個 >>> q.pop() 6 >>> q deque([9, 8, 7, 3, 1, 2, 4, 5]) # 刪除佇列中最左邊的一個元素 >>> q.popleft() 9 >>> q deque([8, 7, 3, 1, 2, 4, 5]) |
其他的API
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# 清空佇列 >>> q deque([8, 7, 3, 1, 2, 4, 5]) >>> q.clear() >>> q deque([]) # 判斷佇列是否為空 >>> not q True # 獲取佇列最大長度 >>> q = deque([1,2], 10) >>> q.maxlen 10 # 檢視某個元素出現的次數 >>> q.extend([1,2,1,1]) >>> q.count(1) 4 # 檢視當前佇列長度 >>> len(q) 6 # 判斷佇列是否滿了 >>> q.maxlen == len(q) False # 佇列元素反轉 >>> q = deque([1,2,3,4,5],5) >>> q.reverse() >>> q deque([5, 4, 3, 2, 1], maxlen=5) # 檢視元素對應的索引 >>> q.index(1) 4 # 刪除匹配到的第一個元素 >>> q deque([5, 4, 3, 2, 1], maxlen=5) >>> q.remove(5) >>> q deque([4, 3, 2, 1], maxlen=5) # 元素位置進行旋轉 >>> q deque([4, 3, 2, 1], maxlen=5) >>> q.rotate(2) >>> q deque([2, 1, 4, 3], maxlen=5) >>> q.rotate(1) >>> q deque([3, 2, 1, 4], maxlen=5) # 使用負數 >>> q.rotate(-1) >>> q deque([2, 1, 4, 3], maxlen=5) |
例項
二項式係數
題目
編寫程式,求二項式係數表中(楊輝三角)第K層系列數
1 2 3 4 5 |
1 1 1 1 2 1 1 3 3 1 ...... |
思路
- 把第K行的係數儲存在佇列中
- 依次出隊K層的係數(每行最後一個1不出隊),並推算K+1層係數,新增到隊尾,最後在隊尾新增一個1,便變成了k+1行。
解決程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#!/use/bin/env python # _*_ coding:utf-8 _*_ from collections import deque def yanghui(k): """ :param k: 楊輝三角中第幾層 :return: 第K層的係數 """ q = deque([1]) # 建立一個佇列,預設從1開始 for i in range(k): # 迭代要查詢的層數 for _ in range(i): # 迴圈需要出隊多少次 q.append(q.popleft() + q[0]) # 第一個數加上佇列中第二個數並賦值到佇列末尾 q.append(1) # 每次查詢結束後都需要在佇列最右邊新增個1 return list(q) result = yanghui(3) print(result) |
劃分無衝突子集
題目
某動物園搬家,要運走N種動物,老虎與獅子放在一起會大家,大象與犀牛放在一個籠子會打架,野豬和野狗放在一個籠子裡會打架,現在需要我們設計一個演算法,使得裝進同一個籠子的動物互相不打架。
思路
- 把所有動物按次序入隊
- 建立一個籠子(集合),出隊一個動物,如果和籠子內動物無沖沖突則新增到該籠子,有衝突則新增到隊尾,等待進入新籠子
- 由於佇列先進先出的特性,如果當前出隊動物的index不大於前一個出隊動物的index,說明當前佇列中所有動物已經嘗試過進入且進入不了當前籠子,此時建立信的籠子(集合)
解決程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#!/use/bin/env python # _*_ coding:utf-8 _*_ from collections import deque def division(m, n): """ :param m: 衝突關係矩陣 :param n: 幾種動物 :return: 返回一個棧,棧內包含了所有的籠子 """ res = [] # 建立一個棧 q = deque(range(n)) # 初始化佇列,裡面放著動物的序號 pre = n # 前一個動物的下標 while q: cur = q.popleft() # 從隊頭出隊一個動物 if pre >= cur: # 是否需要建立籠子 res.append([]) # 建立一個籠子 # 當前的動物是否與籠子內的動物有衝突 for a in res[-1]: # 迭代棧中最頂層的籠子 if m[cur][a]: # 有衝突 q.append(cur) # 重新放入佇列的尾部 break else: # 當前動物和當前籠子中的所有動物沒衝突 res[-1].append(cur) # 當前動物放入最上面的籠子中 pre = cur # 當前變成之前的 return res N = 9 R = { # 衝突對應關係表 (1, 4), (4, 8), (1, 8), (1, 7), (8, 3), (1, 0), (0, 5), (1, 5), (3, 4), (5, 6), (5, 2), (6, 2), (6, 4), } M = [[0] * N for _ in range(N)] # 沖洗關係矩陣M,0代表不衝突 for i, j in R: M[i][j] = M[j][i] = 1 # 1代表衝突 result = division(M, N) print(result) |
數字變換
題目
對於一對正整數a,b,對a只能進行加1,減1,乘2操作,問最少對a進行幾次操作能得到b?
例如:
- a=3,b=11: 可以通過322-1,3次操作得到11;
- a=5,b=8:可以通過(5-1)*2,2次操作得到8;
思路
本題用廣度優先搜尋,尋找a到b狀態遷移最短路徑,對於每個狀態s,可以轉換到撞到s+1,s-1,s*2:
- 把初始化狀態a入隊;
- 出隊一個狀態s,然後s+1,s-1,s*2入隊;
- 反覆迴圈第二步驟,直到狀態s為b;
解決程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#!/use/bin/env python # _*_ coding:utf-8 _*_ from collections import deque def atob(a, b): """ :param a: 開始的數字 :param b: 最終轉換之後的數字 :return: 最小匹配的次數 """ q = deque([(a, 0)]) # a=當前數字,0=操作的次數 checked = {a} # 已經檢查過的資料 while True: s, c = q.popleft() if s == b: break if s < b: # 要計算的數小於計算之後的數字 if s + 1 not in checked: # 如果要計算的數字+1不在已檢查過的資料集合中 q.append((s + 1, c + 1)) # 要計算的數+1,轉換次數+1 checked.add(s + 1) # 把計算過的數新增到checked集合中 if s * 2 not in checked: q.append((s * 2, c + 1)) checked.add(s * 2) if s > 0: # 要計算的數大於0 if s - 1 not in checked: q.append((s - 1, c + 1)) checked.add(s - 1) return q.popleft()[-1] result = atob(3, 11) print(result) |