如果你只有固定的一塊記憶體空間能用來當作 queue,則可以把原本一直線的空間,改想像成環狀來使用,稱為環狀佇列(circular queue)。也就是說,對於固定的一塊大小為 n 的空間,原版的 queue 在 enqueue 了 n 次以後,即使用到 index 為 n - 1 的位置以後,就無法再 enqueue;而對於 circular queue 來說,若你先前有 dequeue 過,空出了 index 0 的位置時,則可以讓你 enqueue 至該位置。在具體應用方面,則如嵌入式系統之間傳送資料時,或者音訊處理時,需要錄音或播放等等的緩衝區,都可能使用 circular queue。
以下直接用物件的方式,來展示 circular queue 的實作:
class CircularQueue: def __init__(self, capacity=10): assert capacity > 0 self.capacity = capacity self.q = [None for _ in range(self.capacity)] self.size = 0 self.head = 0 self.tail = 0 def enqueue(self, v): if self.size == self.capacity: return None self.q[self.tail] = v self.tail = (self.tail + 1) % self.capacity self.size += 1 return 0 def dequeue(self): if self.size == 0: return None r = self.q[self.head] self.head = (self.head + 1) % self.capacity self.size -= 1 return r Q = CircularQueue(50) Q.enqueue(3) Q.enqueue(4) print(Q.dequeue()) # 3 print(Q.dequeue()) # 4 print(Q.dequeue()) # None在上述範例中,由於 head 跟 tail 相等時,可能代表 queue 是滿的,也可能代表 queue 是空的,因此我們用一個額外的變數,來記錄現在 queue 裡面裝了多少東西,但也有其他做法,是以犧牲一格,或者多使用一些邏輯運算,來避免空跟滿的狀態有誤判。而關於時間複雜度,由於 enqueue 和 dequeue 都只有執行固定動作,與 queue 的大小等狀況無關,所以是 O(1)。
Queue 的另一種變形是雙向佇列(double-ended queue,簡稱 deque)。基本的 queue 只能由其中一邊進,然後由另一邊出,deque 則是兩邊都可以進出;具體應用則例如網路傳輸中,若要處理即時語音封包等需要緊急插隊處理的狀況時,可以使用 deque。
這個資料結構在 Python 內建的 collections 當中已經有實作。以下直接示範如何使用,下列示範的動作,時間複雜度都是 O(1):
from collections import deque d = deque() d.append(3) d.append(4) print('After append:', d) d.appendleft(5) d.appendleft(6) print('After appendleft:', d) print('Tail element:', d[-1]) r = d.pop() print('Pop:', r, d) print('Head element:', d[0]) r = d.popleft() print('Popleft:', r, d) print('Size:', len(d))Note: 請試著在 deque 理應為空的時候執行 pop 或 popleft,看看是否會發生 error。