🚀 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.

appendleft() ⬅️ [ DEQUE ] ➡️ append()
popleft() ⬅️ [ DEQUE ] ➡️ pop()

🤔 Deque'nin Problemi: Çok Esnek!

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

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ı.

✅ Çö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()

Bu şekilde kullanıcı yanlışlıkla kuralları bozamaz!

🛠️ Queue Wrapper Sınıfı

Python Kodu
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}")
Çıktı bekleniyor...

📚 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:
    """
    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()
Çıktı bekleniyor...

🧪 Performans Yarışı: List vs Deque

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

Python Kodu
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ı!")
Çıktı bekleniyor...

📊 Ö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)