8 - Kuyruk İşlemleri ve Örnekleri
2026
Geçen hafta kuyruğun temel mantığını ve enqueue işlemini gördük.
Bu hafta özellikle şunları netleştireceğiz:
dequeuepeekisEmptyhead hem tail güncellenmelidir?Bu derste yalnızca işlemleri değil, kuyruğun durumunu doğru yönetmeyi ele alacağız.
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
struct Node {
int veri;
struct Node *sonraki;
};
struct Node *head = NULL; // Kuyruğun başı: çıkarma buradan yapılır
struct Node *tail = NULL; // Kuyruğun sonu: ekleme buraya yapılır
void enqueue(int sayi) {
struct Node *yeniDugum = malloc(sizeof(struct Node));
if (yeniDugum == NULL) {
printf("Hata: Bellek ayrılamadi!\n");
return;
}
yeniDugum->veri = sayi;
yeniDugum->sonraki = NULL;
if (tail == NULL) {
head = yeniDugum;
tail = yeniDugum;
} else {
tail->sonraki = yeniDugum;
tail = yeniDugum;
}
printf("%d kuyruğa eklendi.\n", sayi);
}head ilk düğümü gösterir.tail son düğümü gösterir.printf kullanıyoruz.dequeue İşlemi Nedir?dequeue, kuyruğun başındaki (head) elemanı çıkarır ve bu elemanın verisini döndürür.
Örnek:
Başlangıç:
head tail
| |
v v
[10] -> [20] -> [30] -> NULL
dequeue() sonrası:
head tail
| |
v v
[20] -> [30] -> NULLBu işlemde kuyruktan çıkan değer 10 olur.
Kuyruk boşsa önce bunu kontrol etmek gerekir.
dequeue Fonksiyonunun Mantığıhead düğümünü geçici bir işaretçide tut.head işaretçisini bir sonraki düğüme kaydır.tail = NULL yap.free ile serbest bırak.Buradaki en kritik nokta, son eleman çıkarıldığında yapının tamamen boşaldığını doğru yansıtmaktır.
dequeue FonksiyonuTek elemanlı kuyrukta head ve tail aynı düğümü gösterir:
Bu eleman dequeue() ile çıkarıldığında yapı tamamen boşalır.
Bu yüzden yalnızca head = NULL yapmak yetmez.
tail = NULL da yapılmalıdır.
Aksi halde tail, artık geçerli olmayan bir düğümü gösteriyor gibi kalır.
peek İşlemi: Çıkarmadan Bakmakpeek, kuyruğun başındaki elemanı çıkarmadan döndürür.
int peek() {
if (head == NULL) {
printf("Hata: Kuyruk boş!\n");
return INT_MIN;
}
printf("Kuyruğun başındaki eleman: %d\n", head->veri);
return head->veri;
}Burada yapı değişmez.
Yani peek() ile dequeue() arasındaki temel fark şudur:
peek() sadece bakar,dequeue() elemanı gerçekten çıkarır.isEmpty İşlemi: Kuyruk Boş mu?10Boşluk kontrolü için head == NULL yazmak yeterlidir.
Yapı doğru yönetiliyorsa kuyruk boş olduğunda tail de zaten NULL olur.
Bu örneklerde enqueue, dequeue ve peek içinde printf kullandık.
Bunu derste özellikle tercih ediyoruz; çünkü işlemin etkisini ekranda hemen görebiliyoruz.
Ama daha gerçekçi programlarda veri yapısı fonksiyonları çoğu zaman:
Yazdırma işi genellikle main gibi istemci kodda yapılır.
INT_MIN ile Hata Bildirmedequeue() ve peek() normalde bir int döndürür.
Ama kuyruk boşsa ortada döndürülecek gerçek bir eleman yoktur.
Bu örnekte hata durumunu göstermek için INT_MIN kullanıyoruz.
Bu yaklaşım basittir:
Fakat önemli bir sınırlaması vardır:
Eğer kuyrukta gerçekten INT_MIN değeri tutuluyorsa, program bunun hata mı yoksa gerçek veri mi olduğunu ayırt edemez.
Bu karışıklığı önlemek için bazı programlarda:
Böylece hata bilgisi ile gerçek veri birbirine karışmaz.
dequeueSafe Örneğiint dequeueSafe(int *sonuc) {
if (head == NULL) {
return 0;
}
struct Node *temp = head;
*sonuc = temp->veri;
head = head->sonraki;
if (head == NULL) {
tail = NULL;
}
free(temp);
return 1;
}Burada:
return 1 -> işlem başarılıreturn 0 -> kuyruk boş*sonuc -> gerçekten çıkarılan veriBu örnekte sonuc için geçerli bir adres verildiğini varsayıyoruz.
Aşağıdaki işlem sırasını düşünelim:
enqueue(10)enqueue(20)enqueue(30)peek()dequeue()peek()dequeue()enqueue(40)Burada yalnızca dönen değerlere değil, her adımdan sonra kuyruğun yapısının nasıl değiştiğine dikkat etmek gerekir.
main fonksiyonu)int main() {
printf("Başlangıçta kuyruk boş mu? %s\n", isEmpty() ? "Evet" : "Hayır");
enqueue(10);
enqueue(20);
enqueue(30);
printf("Kuyruk boş mu? %s\n", isEmpty() ? "Evet" : "Hayır");
peek();
dequeue();
peek();
dequeue();
enqueue(40);
peek();
dequeue();
dequeue();
printf("Son durumda kuyruk boş mu? %s\n", isEmpty() ? "Evet" : "Hayır");
dequeue();
return 0;
}Bu örnekte asıl amaç, programın sadece çalışması değildir.
Şunları gözlemlemek istiyoruz:
peek() yapıyı değiştirmezdequeue() her zaman baştan eleman çıkarırYani burada odak noktamız çıktı ezberlemek değil, yapının davranışını takip etmektir.
| İşlem | Kuyruğun durumu | Not |
|---|---|---|
| başlangıç | boş | head = NULL, tail = NULL |
enqueue(10) |
10 |
ilk eleman |
enqueue(20) |
10 -> 20 |
sona ekleme |
enqueue(30) |
10 -> 20 -> 30 |
sona ekleme |
peek() |
10 -> 20 -> 30 |
yapı değişmez |
dequeue() |
20 -> 30 |
baştaki eleman çıkar |
peek() |
20 -> 30 |
yapı değişmez |
dequeue() |
30 |
tekrar baştan çıkarma |
enqueue(40) |
30 -> 40 |
sona ekleme |
Bu tablo şunu açık gösterir:
peek() yapıyı değiştirmez,dequeue() baştaki elemanı çıkarır,dequeue sonrası eski düğümü free etmemektail = NULL güncellemesini unutmakpeek ile dequeue işlemini aynı şey sanmakINT_MIN yaklaşımını her durumda güvenli zannetmekhttps://gist.github.com/ecylmz/dd3b7086fc80582ccce7b09034275574
Bu bağlantıda, derste gördüğümüz enqueue, dequeue, peek ve isEmpty fonksiyonlarının tam hali birlikte yer alır.
1. peek() işlemi hangisini yapar?
2. Kuyrukta yalnızca 42 varken dequeue() çağrılıyor. İşlem tamamlandıktan sonra tail hangi değeri almalıdır?
42NULLhead + 13. Aşağıdaki işlem sırası uygulandıktan sonra peek() hangi değeri döndürür?
enqueue(10)
enqueue(20)
dequeue()
enqueue(30)
peek()
102030dequeue fonksiyonu boş kuyruk durumunda INT_MIN döndürmek yerine void olsa ve sadece hata mesajı bassa nasıl olurdu? Avantajları ve dezavantajları nelerdir?dequeueSafe fonksiyonunu kullanan kısa bir main fonksiyonu yazın. İşlem başarılıysa çıkarılan değeri yazdırın, başarısızsa "Kuyruk boş" mesajı verin.