🚀 3.3: Python Deque ve Wrapper Sınıflar

Neden Hazır Yapıları Sarmalamalıyız?

⚠️ Neden Python List Queue Olamaz?

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.

📊 Kaydırma (Shifting) Maliyeti

  • 10 elemanlı listede: 9 kaydırma işlemi
  • 1,000 elemanlı listede: 999 kaydırma işlemi
  • 1,000,000 elemanlı listede: 999,999 kaydırma işlemi!

Sonuç: list.pop(0)O(n) zaman karmaşıklığı

✅ Çözüm: collections.deque

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.

🟢 EKLEME İşlemleri (O(1))

appendleft(x)
Başa ekle
A B C
append(x)
Sona ekle

🔴 ÇIKARMA İşlemleri (O(1))

popleft()
Baştan çıkar
A B C
pop()
Sondan çıkar

🤔 Deque'nin Problemi: Çok Esnek!

❌ Sorun: Kullanıcı Kısıtlanmıyor

deque hem Stack hem Queue gibi kullanılabilir, ama bu tehlikelidir:

Veri yapısı kuralları ihlal edildi! Queue FIFO olmalı, ama kullanıcı LIFO işlemi yaptı.

✅ Çözüm: Wrapper Class ile Kısıtlama

Kullanıcıya sadece izin verilen metodları sunarak veri yapısı kurallarını zorla:

  • Queue için: Sadece enqueue() ve dequeue()
  • Stack için: Sadece push() ve pop()

🛠️ Queue Wrapper Sınıfı

🐍 Python Kodu
from collections import deque

class Queue:
    """
    FIFO (First In, First Out) Queue implementasyonu.
    Sadece izin verilen metodları sunar.
    """
    def __init__(self):
        self._data = deque()

    def enqueue(self, item):
        """Kuyruğun sonuna eleman ekler"""
        self._data.append(item)
        print(f"✅ Eklendi: {item} → Kuyruk: {list(self._data)}")

    def dequeue(self):
        """Kuyruğun başından eleman çıkarır"""
        if self.is_empty():
            raise IndexError("Kuyruk boş!")
        item = self._data.popleft()
        print(f"🔴 Çıkarıldı: {item} ← Kuyruk: {list(self._data)}")
        return item

    def peek(self):
        """Baştaki elemana bakar (çıkarmaz)"""
        if self.is_empty():
            return None
        return self._data[0]

    def is_empty(self):
        return len(self._data) == 0

    def __len__(self):
        return len(self._data)

# Test edelim
print("=" * 40)
print("🚀 Queue Wrapper Testi")
print("=" * 40)

q = Queue()
q.enqueue("Müşteri 1")
q.enqueue("Müşteri 2")
q.enqueue("Müşteri 3")

print(f"\n📌 Sıradaki: {q.peek()}")
print(f"📊 Kuyruk uzunluğu: {len(q)}")

print("\n--- Sıra işleniyor ---")
while not q.is_empty():
    q.dequeue()

📚 Stack Wrapper Sınıfı

Aynı deque'yi Stack için de kullanabiliriz, ama farklı metodlar sunarız:

🐍 Python Kodu
from collections import deque

class Stack:
    """
    LIFO (Last In, First Out) Stack implementasyonu.
    Sadece izin verilen metodları sunar.
    """
    def __init__(self):
        self._data = deque()

    def push(self, item):
        """Yığının tepesine eleman ekler"""
        self._data.append(item)
        print(f"📥 Push: {item} → Stack: {list(self._data)}")

    def pop(self):
        """Yığının tepesinden eleman çıkarır"""
        if self.is_empty():
            raise IndexError("Stack boş!")
        item = self._data.pop()
        print(f"📤 Pop: {item} ← Stack: {list(self._data)}")
        return item

    def top(self):
        """Tepedeki elemana bakar (çıkarmaz)"""
        if self.is_empty():
            return None
        return self._data[-1]

    def is_empty(self):
        return len(self._data) == 0

    def __len__(self):
        return len(self._data)

# Test edelim
print("=" * 40)
print("📚 Stack Wrapper Testi")
print("=" * 40)

s = Stack()
s.push("Tabak 1")
s.push("Tabak 2")
s.push("Tabak 3")

print(f"\n📌 Tepedeki: {s.top()}")
print(f"📊 Stack uzunluğu: {len(s)}")

print("\n--- Tabaklar alınıyor (LIFO) ---")
while not s.is_empty():
    s.pop()

🧪 Performans Yarışı: List vs Deque

10,000 elemanı baştan silme yarışı. List neden kaybedecek?

🐍 Python Kodu
import time
from collections import deque

N = 10000  # Eleman sayısı

print("=" * 50)
print(f"🏁 PERFORMANS YARIŞI: {N:,} eleman baştan silme")
print("=" * 50)

# --- LIST TESTİ ---
print("\n📋 List testi başlıyor...")
test_list = list(range(N))

start = time.time()
while test_list:
    test_list.pop(0)  # O(n) - her silmede kaydırma!
list_time = time.time() - start

print(f"⏱️  List süresi: {list_time:.4f} saniye")

# --- DEQUE TESTİ ---
print("\n🔗 Deque testi başlıyor...")
test_deque = deque(range(N))

start = time.time()
while test_deque:
    test_deque.popleft()  # O(1) - sabit zaman!
deque_time = time.time() - start

print(f"⏱️  Deque süresi: {deque_time:.4f} saniye")

# --- SONUÇLAR ---
print("\n" + "=" * 50)
print("📊 SONUÇLAR")
print("=" * 50)

if list_time > 0 and deque_time > 0:
    hiz_farki = list_time / deque_time
    print(f"🐢 List:  {list_time:.4f} saniye")
    print(f"🚀 Deque: {deque_time:.4f} saniye")
    print(f"\n✨ Deque, List'ten {hiz_farki:.1f}x daha hızlı!")
    print(f"\n💡 Neden? List O(n), Deque O(1) karmaşıklık!")

📊 Özet: Ne Zaman Ne Kullan?

İhtiyaç Kullanım Neden?
Queue (FIFO) Queue wrapper Kullanıcı kısıtlanır, sadece enqueue/dequeue
Stack (LIFO) Stack wrapper Kullanıcı kısıtlanır, sadece push/pop
İki uçlu işlem deque doğrudan Hem baş hem son gerekli
Random access list Index ile erişim O(1)