Neden Hazır Yapıları Sarmalamalıyız?
Python listeleri hafızada bitişik (contiguous) durur. Bu, C'deki dizilere benzer bir yapıdır.
pop(0) ile en baştan eleman silerseniz, geriye kalan TÜM elemanlar bir adım sola kaymak zorundadır.
Sonuç: list.pop(0) → O(n) zaman karmaşıklığı
Deque (Double Ended Queue), iki ucu açık boru gibidir. İçeride doubly linked list benzeri bir yapı kullanır.
Hem baştan hem sondan ekleme/çıkarma işlemleri O(1) sabit zamanda gerçekleşir.
deque hem Stack hem Queue gibi kullanılabilir, ama bu tehlikelidir:
# Diyelim ki "Queue" olarak kullanmak istiyoruz
from collections import deque
kuyruk = deque()
kuyruk.append("Ali") # ✅ Doğru: Sona ekle
kuyruk.append("Veli") # ✅ Doğru: Sona ekle
kuyruk.popleft() # ✅ Doğru: Baştan çıkar (FIFO)
# AMA kullanıcı yanlışlıkla:
kuyruk.pop() # ❌ YANLIŞ! Sondan çıkardı (Stack gibi!)
kuyruk.appendleft("X") # ❌ YANLIŞ! Başa ekledi (Queue'da yasak!)
Veri yapısı kuralları ihlal edildi! Queue FIFO olmalı, ama kullanıcı LIFO işlemi yaptı.
Kullanıcıya sadece izin verilen metodları sunarak veri yapısı kurallarını zorla:
enqueue() ve dequeue()push() ve pop()Bu şekilde kullanıcı yanlışlıkla kuralları bozamaz!
from collections import deque
class Queue:
"""
Deque tabanlı Queue wrapper.
Kullanıcıya sadece Queue operasyonları sunulur.
"""
def __init__(self):
# Alt çizgi ile başlayan isim: "Bu değişkene dokunma!" demek
self._items = deque()
def enqueue(self, item):
"""Sıranın SONUNA ekler - O(1)"""
self._items.append(item)
print(f"→ Enqueue: {item}")
def dequeue(self):
"""Sıranın BAŞINDAN çıkarır - O(1)"""
if self.is_empty():
raise IndexError("Queue boş!")
item = self._items.popleft() # ← Kritik: popleft kullanılıyor!
print(f"← Dequeue: {item}")
return item
def peek(self):
"""Başındaki elemana BAK (çıkarma)"""
if self.is_empty():
return None
return self._items[0]
def is_empty(self):
return len(self._items) == 0
def size(self):
return len(self._items)
def __repr__(self):
return f"Queue({list(self._items)})"
# ===== KULLANIM =====
print("=" * 50)
print("📋 Queue Wrapper Kullanımı")
print("=" * 50)
q = Queue()
q.enqueue("Ali")
q.enqueue("Veli")
q.enqueue("Ayşe")
print(f"\nQueue durumu: {q}")
print(f"Sıradaki (peek): {q.peek()}")
print(f"Boyut: {q.size()}")
print(f"\nDequeue işlemleri:")
while not q.is_empty():
q.dequeue()
print(f"\nQueue durumu: {q}")
Aynı deque'yi Stack için de kullanabiliriz, ama farklı metodlar sunarız:
from collections import deque
class Stack:
"""
Deque tabanlı Stack wrapper.
LIFO: Son giren ilk çıkar.
"""
def __init__(self):
self._items = deque()
def push(self, item):
"""Yığının tepesine ekler - O(1)"""
self._items.append(item)
print(f"↓ Push: {item}")
def pop(self):
"""Yığının tepesinden çıkarır - O(1)"""
if self.is_empty():
raise IndexError("Stack boş!")
item = self._items.pop() # ← Stack için: pop() (sondan çıkar)
print(f"↑ Pop: {item}")
return item
def peek(self):
"""Tepedeki elemana BAK (çıkarma)"""
if self.is_empty():
return None
return self._items[-1] # Son eleman = tepe
def is_empty(self):
return len(self._items) == 0
# ===== Stack vs Queue Karşılaştırması =====
print("=" * 50)
print("📚 Stack (LIFO) vs 📋 Queue (FIFO)")
print("=" * 50)
# Aynı elemanları ekleyelim
print("\n📚 STACK:")
s = Stack()
s.push(1)
s.push(2)
s.push(3)
print("Çıkarma sırası (LIFO):")
while not s.is_empty():
s.pop()
print("\n📋 QUEUE:")
q = Queue()
q.enqueue(1)
q.enqueue(2)
q.enqueue(3)
print("Çıkarma sırası (FIFO):")
while not q.is_empty():
q.dequeue()
100,000 elemanı baştan silme yarışı. List neden kaybedecek?
import time
from collections import deque
N = 100_000
print(f"🏁 {N} eleman baştan silinecek...\n")
# 1. LIST (Kötü)
liste = list(range(N))
start = time.time()
while liste:
liste.pop(0) # O(n) - her seferinde kaydırma!
list_time = time.time() - start
print(f"📋 List pop(0) : {list_time:.4f} saniye")
# 2. DEQUE (İyi)
kuyruk = deque(range(N))
start = time.time()
while kuyruk:
kuyruk.popleft() # O(1) - sabit zaman!
deque_time = time.time() - start
print(f"🔄 Deque popleft: {deque_time:.4f} saniye")
kat = list_time / deque_time
print(f"\n🚀 Sonuç: Deque {kat:.0f}x daha hızlı!")