Çift Uçlu Kuyruk (Deque)
Hem önden hem sondan O(1) ekleme ve çıkarma yapılabilen iki uçlu kuyruk.
Önce bunlara göz at
#Nasıl çalışır?
Deque (double-ended queue, iki uçlu kuyruk), elemanların hem önden hem sondan eklenip çıkarılabildiği bir veri yapısıdır. Yığın (stack) ile kuyruğun (queue) birleşimi gibi düşünebilirsin.
Yalnızca sona ekleyip önden çıkarırsan kuyruk, yalnızca tek uçtan ekleyip çıkarırsan yığın gibi davranır. Bu esneklik onu birçok algoritmanın temel yapı taşı yapar.
Genellikle dairesel dizi (circular buffer) ya da çift yönlü bağlı listeyle gerçeklenir; böylece dört işlem de O(1) sürer.
#Kendin dene
Deque iki uçlu bir kuyruktur: hem öne hem sona ekleyip hem önden hem sondan çıkarabilirsin.
#Temel işlemler
- pushFront(x)
- x değerini en öne ekler.
- pushBack(x)
- x değerini en sona ekler.
- popFront()
- Öndeki elemanı çıkarır ve döndürür.
- popBack()
- Sondaki elemanı çıkarır ve döndürür.
- peekFront() / peekBack()
- Uçtaki elemanı çıkarmadan okur.
#Örnek kod
Aynı algoritma 8 dilde. Her program derlenip çalıştırılarak test edildi ve hepsi aynı çıktıyı veriyor.
class Deque:
"""Circular buffer: both ends wrap around, so nothing is ever shifted.
(The standard library already has one: collections.deque.)
"""
def __init__(self, capacity: int) -> None:
self._items = [0] * capacity
self._front = 0
self._size = 0
def push_front(self, value: int) -> None:
if self._size == len(self._items):
raise OverflowError("deque is full")
self._front = (self._front - 1) % len(self._items)
self._items[self._front] = value
self._size += 1
def push_back(self, value: int) -> None:
if self._size == len(self._items):
raise OverflowError("deque is full")
self._items[(self._front + self._size) % len(self._items)] = value
self._size += 1
def pop_front(self) -> int:
if self._size == 0:
raise IndexError("deque is empty")
value = self._items[self._front]
self._front = (self._front + 1) % len(self._items)
self._size -= 1
return value
def pop_back(self) -> int:
if self._size == 0:
raise IndexError("deque is empty")
self._size -= 1
return self._items[(self._front + self._size) % len(self._items)]
def __len__(self) -> int:
return self._size
d = Deque(8)
d.push_back(1)
d.push_back(2)
d.push_front(0) # [0, 1, 2]
print(d.pop_back()) # 2
print(d.pop_front()) # 0
print(len(d)) # 1#Karmaşıklık
| İşlem | Süre |
|---|---|
| pushFront / pushBack | O(1) |
| popFront / popBack | O(1) |
| Ortadaki bir elemana erişmek | O(1) dizi / O(n) liste |
| Bellek | O(n) |
Dairesel dizide front indeksi geriye, arka indeks ileriye kayar; hiçbir eleman taşınmaz. Dizi dolunca kapasite iki katına çıkarılır, bu yüzden ekleme amortize O(1) olur.
#Ne zaman kullanılır?
Kayan pencere (sliding window) maksimumu, 0-1 BFS, palindrom kontrolü, geri al/yinele geçmişi ve iş çalma (work stealing) zamanlayıcıları deque kullanır. Python'da collections.deque, C++'ta std::deque, Java'da ArrayDeque hazır gerçeklemelerdir.
#Alıştırma
Soruları cevaplamak ve ilerlemeni kaydetmek için giriş yap.
Kolay
Deque'de hangi uçlardan eleman eklenip çıkarılabilir?
- 1Hem önden hem sondan
- 2Yalnızca sondan
- 3Yalnızca önden
- 4Yalnızca ortadan
Dairesel diziyle gerçeklenen bir deque'de pushFront kaç adım sürer?
- 1O(1)
- 2O(n)
- 3O(log n)
- 4O(n log n)
Deque'yi yalnızca pushBack ve popBack ile kullanırsan hangi yapıya dönüşür?
- 1Yığın (stack)
- 2Kuyruk (queue)
- 3Heap
- 4Trie
Orta
Boş deque: pushBack(1), pushBack(2), pushFront(0), popBack(). Hangi değer çıkar?
- 12
- 20
- 31
- 4Hiçbiri
Zor
Kayan pencere maksimumu (sliding window maximum) probleminde deque neden kullanılır?
- 1Pencereden çıkanlar önden, işe yaramayan küçükler sondan O(1) atılabildiği için
- 2Elemanları otomatik sıraladığı için
- 3Arama O(1) olduğu için
- 4Daha az bellek kullandığı için