Neden Dairesel Kuyruk Gerekli?
Bir diziyi queue olarak kullandığımızda head (baş) ve tail (kuyruk) pointer'ları kullanırız:
import numpy as np
class LinearQueue:
"""
Normal (Dairesel Olmayan) Queue
Problem: tail sona ulaştığında, başta yer olsa bile ekleme yapılamaz!
"""
def __init__(self, capacity):
self.capacity = capacity
self.data = np.zeros(capacity, dtype=int) # Sabit boyutlu numpy dizisi
self.head = 0 # Çıkış noktası (dequeue buradan)
self.tail = 0 # Giriş noktası (enqueue buraya)
self.size = 0 # Mevcut eleman sayısı
def enqueue(self, val):
# ❌ PROBLEM: tail dizinin sonuna ulaştıysa,
# baş tarafta boş yer olsa bile ekleme yapamayız!
if self.tail == self.capacity:
print(f"❌ Dolu! tail={self.tail} (Dizinin sonuna ulaşıldı)")
print(f" Başta {self.head} boş hücre var ama kullanamıyoruz!")
return False
self.data[self.tail] = val
self.tail += 1 # Sadece artırıyoruz, modülo YOK
self.size += 1
print(f"✅ Enqueue({val}) -> Dizi: {self.data}, head={self.head}, tail={self.tail}")
return True
def dequeue(self):
if self.size == 0:
print("❌ Boş!")
return None
val = self.data[self.head]
self.data[self.head] = 0 # Görsel temizlik (zorunlu değil)
self.head += 1 # Sadece artırıyoruz, modülo YOK
self.size -= 1
print(f"🔻 Dequeue() -> {val}, Dizi: {self.data}, head={self.head}, tail={self.tail}")
return val
# ===== PROBLEMİ GÖSTEREN SENARYO =====
print("=" * 60)
print("📌 NORMAL QUEUE PROBLEMİ")
print("=" * 60)
q = LinearQueue(5)
# Adım 1: 3 eleman ekle
print("\n1️⃣ Üç eleman ekliyoruz:")
q.enqueue(10)
q.enqueue(20)
q.enqueue(30)
# Adım 2: 2 eleman çıkar
print("\n2️⃣ İki eleman çıkarıyoruz:")
q.dequeue() # 10 çıkar
q.dequeue() # 20 çıkar
# Adım 3: 2 eleman daha ekle
print("\n3️⃣ İki eleman daha ekliyoruz:")
q.enqueue(40)
q.enqueue(50)
# Adım 4: Bir eleman daha eklemeye çalış
print("\n4️⃣ Bir eleman daha eklemeye çalışıyoruz:")
q.enqueue(60) # ❌ BAŞARISIZ! Başta yer var ama kullanamıyoruz!
print("\n" + "=" * 60)
print("📊 SONUÇ:")
print(f" Dizide sadece {q.size} eleman var")
print(f" Ama {q.head} hücre boşa gitti (kullanılamıyor)!")
print(" Bu hafıza israfıdır ve 'false overflow' denir.")
print("=" * 60)
Başlangıç durumu (3 eleman eklendi):
2 eleman çıkarıldıktan sonra:
⚠️ tail=5 oldu ama dizi boyutu 5. Başta 2 hücre boş olmasına rağmen yeni eleman ekleyemiyoruz!
Pointer'ları % capacity ile sarmalayarak dizinin başına dönebiliriz:
tail = (tail + 1) % capacity # 5 % 5 = 0 (başa döner!)
head = (head + 1) % capacity
Bu sayede dizi dairesel olarak kullanılır ve hafıza israfı önlenir.
Head: 0, Tail: 0
import numpy as np
class CircularQueue:
def __init__(self, capacity):
self.capacity = capacity
self.data = np.zeros(capacity, dtype=int)
self.head = 0
self.tail = 0
self.size = 0
def enqueue(self, val):
if self.size == self.capacity:
print("❌ Gerçekten dolu! (Tüm hücreler kullanımda)")
return False
self.data[self.tail] = val
# 🔑 MODÜLO İLE SARMALAMA - dizinin başına döner!
self.tail = (self.tail + 1) % self.capacity
self.size += 1
print(f"✅ Enqueue({val}) -> Dizi: {self.data}, head={self.head}, tail={self.tail}")
return True
def dequeue(self):
if self.size == 0:
print("❌ Boş!")
return None
val = self.data[self.head]
self.data[self.head] = 0
# 🔑 MODÜLO İLE SARMALAMA
self.head = (self.head + 1) % self.capacity
self.size -= 1
print(f"🔻 Dequeue() -> {val}, Dizi: {self.data}, head={self.head}, tail={self.tail}")
return val
# ===== CIRCULAR QUEUE'NUN AVANTAJINI GÖSTER =====
print("=" * 60)
print("📌 CIRCULAR QUEUE - AYNI SENARYO")
print("=" * 60)
q = CircularQueue(5)
print("\n1️⃣ Üç eleman ekliyoruz:")
q.enqueue(10)
q.enqueue(20)
q.enqueue(30)
print("\n2️⃣ İki eleman çıkarıyoruz:")
q.dequeue()
q.dequeue()
print("\n3️⃣ İki eleman daha ekliyoruz:")
q.enqueue(40)
q.enqueue(50)
print("\n4️⃣ Şimdi bir eleman daha eklemeye çalışalım:")
q.enqueue(60) # ✅ BAŞARILI! tail=0'a döndü!
print("\n5️⃣ Hatta bir tane daha ekleyelim:")
q.enqueue(70) # ✅ BAŞARILI! tail=1'e geçti!
print("\n" + "=" * 60)
print("📊 SONUÇ:")
print(f" Circular Queue ile tüm {q.capacity} hücre kullanılabilir!")
print(f" Mevcut eleman sayısı: {q.size}")
print(" Hafıza israfı YOK!")
print("=" * 60)
Modülo, bölme işleminin kalanını verir ve circular yapılar için mükemmeldir:
# Kapasite = 5 iken:
0 % 5 = 0 # Başlangıç
1 % 5 = 1
2 % 5 = 2
3 % 5 = 3
4 % 5 = 4
5 % 5 = 0 # ← Tekrar başa döndü!
6 % 5 = 1
7 % 5 = 2
...
# Bu sayede dizi hiçbir zaman "taşmaz",
# sadece başa döner (wrap-around)
Gerçek hayat analojisi: Saat kadranı! 12'den sonra 1'e döner (12 % 12 = 0, sonra 1, 2, 3...).