📊 3.5: Queue Analizi ve Zaman Karmaşıklığı

Big O Notasyonu ile Performans Değerlendirmesi

⏱️ Zaman Karmaşıklığı Özet Tablosu

Operasyon List Deque Neden?
Enqueue
append()
O(1)* O(1) Sona ekleme: sadece son elemana pointer ekle
Dequeue
pop(0) / popleft()
O(n) ⚠️ O(1) List: Tüm elemanlar kaydırılır
Deque: Sadece HEAD pointer güncellenir
Peek/Front
[0]
O(1) O(1) İndeks erişimi: doğrudan bellek adresine git
isEmpty
len() == 0
O(1) O(1) Boyut değişkeni zaten tutuluyor, sadece oku
Size
len()
O(1) O(1) Boyut değişkeni her ekleme/silmede güncellenir

* List.append() amortize O(1)'dir: Çoğu zaman O(1), nadiren yeniden boyutlandırma gerektiğinde O(n). Ortalaması O(1) olarak kabul edilir.

💾 Alan Karmaşıklığı (Space Complexity)

🔄 Circular Queue

O(n) - Sabit boyut

Bellek kullanımı öngörülebilir. Önceden ayrılmış alan.

📏 Dinamik Queue

O(n) - Değişken boyut

Yeniden boyutlandırma maliyeti. Hafıza parçalanması riski.

🔍 Neden Bazı İşlemler O(1), Bazıları O(n)?

Zaman karmaşıklığını anlamak için veri yapılarının bellekte nasıl tutulduğunu bilmek gerekir.

📦 Python List (Dinamik Dizi) - Bitişik Bellek

Python listesi bellekte yan yana (contiguous) tutulur, tıpkı bir dizi kutu gibi:

Bellek Adresleri:
0x100
A
0x104
B
0x108
C
0x10C
D

❌ list.pop(0) Neden O(n)?

Baştan eleman sildiğinizde, tüm elemanlar bir adres sola kaymak zorunda:

A silinecek
A
B
C
D

n-1 kaydırma işlemi = O(n)

⚠️ Örnek: 1,000,000 elemanlı listede pop(0) = 999,999 kaydırma işlemi!

🔗 collections.deque - Çift Bağlı Liste

Deque, elemanları pointer'larla birbirine bağlar. Bellekte yan yana olmak zorunda değiller:

HEAD
prev: null
A
next: →
prev: ←
B
next: →
prev: ←
C
next: null
TAIL

✅ deque.popleft() Neden O(1)?

Sadece HEAD pointer'ını güncelle, kaydırma yok:

  1. HEAD pointer'ı B'ye taşı
  2. B'nin prev pointer'ını null yap
  3. A'yı bellekten sil

Sadece 3 işlem = O(1) (eleman sayısından bağımsız!)

✨ Örnek: 1,000,000 elemanlı deque'de popleft() = sadece 3 pointer işlemi!

⚡ Özet Karşılaştırma

List pop(0)

O(n)

n eleman = n kaydırma

Deque popleft()

O(1)

n eleman = 3 pointer işlemi

🧪 Performans Karşılaştırma Testi

Farklı Queue implementasyonlarını test edelim ve O(n) vs O(1) farkını gözlemleyelim:

🐍 Python Kodu
import time
from collections import deque

def benchmark_list_queue(n):
    """List ile Queue simülasyonu - O(n) dequeue"""
    q = []

    # Enqueue - O(1) amortized
    start = time.time()
    for i in range(n):
        q.append(i)
    enqueue_time = time.time() - start

    # Dequeue - O(n) her seferinde!
    start = time.time()
    while q:
        q.pop(0)  # Bu O(n) çünkü kaydırma yapıyor
    dequeue_time = time.time() - start

    return enqueue_time, dequeue_time

def benchmark_deque(n):
    """Deque ile Queue - O(1) her işlem"""
    q = deque()

    # Enqueue - O(1)
    start = time.time()
    for i in range(n):
        q.append(i)
    enqueue_time = time.time() - start

    # Dequeue - O(1)
    start = time.time()
    while q:
        q.popleft()  # Bu O(1) çünkü pointer güncellemesi
    dequeue_time = time.time() - start

    return enqueue_time, dequeue_time

# Farklı boyutlarda test
print("=" * 65)
print("🧪 QUEUE PERFORMANS BENCHMARKı")
print("=" * 65)
print(f"{'Eleman':>10} | {'List Dequeue':>15} | {'Deque Dequeue':>15} | {'Fark':>10}")
print("-" * 65)

for n in [1000, 5000, 10000]:
    list_enq, list_deq = benchmark_list_queue(n)
    deque_enq, deque_deq = benchmark_deque(n)

    if deque_deq > 0:
        fark = list_deq / deque_deq
    else:
        fark = float('inf')

    print(f"{n:>10,} | {list_deq:>13.4f}s | {deque_deq:>13.4f}s | {fark:>8.1f}x")

print("-" * 65)
print("\n📊 SONUÇ ANALİZİ:")
print("=" * 65)

print("""
🔴 LIST (pop(0)):
   - Her dequeue'da TÜM elemanlar kaydırılır
   - 10,000 eleman = ~50 milyon kaydırma işlemi (n × n/2)
   - Zaman karmaşıklığı: O(n) per operation → O(n²) toplam

🟢 DEQUE (popleft):
   - Sadece pointer güncellenir
   - 10,000 eleman = 10,000 pointer güncellemesi
   - Zaman karmaşıklığı: O(1) per operation → O(n) toplam

💡 Büyük n'lerde fark KATLANARAK artar!
   - n = 1000 → ~10x fark
   - n = 10000 → ~100x fark
   - n = 100000 → ~1000x fark (tahmin)
""")

📈 Karmaşıklık Grafiği

O(1) - Deque/Circular    O(n) - List pop(0)

💡 Ne Zaman Hangisini Kullanmalı?

Durum Önerilen Neden?
Genel Amaçlı Queue collections.deque O(1) performans, kolay kullanım
Sabit Boyutlu Buffer Circular Queue Bellek öngörülebilirliği, gömülü sistemler
Thread-Safe Queue queue.Queue Dahili kilit mekanizması
Öncelikli İşlemler heapq Priority Queue için optimize