Veri Yapıları ve Programlama

8 - Kuyruk İşlemleri ve Örnekleri

Emre Can Yılmaz

Ondokuz Mayıs Üniversitesi

2026

Geçen Haftanın Özeti

  • Kuyruk (Queue) Nedir? (FIFO Prensibi)
  • Kullanım Alanları
  • Temel Kavramlar (Enqueue, Dequeue, Peek, IsEmpty, IsFull, Front, Rear)
  • Kuyruk Türleri
  • Bağlı Liste ile Temel Yapı (struct Node, head, tail)
  • enqueue (sonaEkle) İşlemi Örneği

Bu Hafta Ne Öğreneceğiz?

Bu dersin sonunda şunları yapabiliyor olmanız beklenir:

  • dequeue, peek ve isEmpty işlemlerinin ne yaptığını açıklamak,
  • tek elemanlı kuyrukta neden hem head hem tail güncellenmesi gerektiğini görmek,
  • boş kuyruk durumunun neden özel olarak ele alınması gerektiğini anlamak,
  • C dilinde hata değeri döndürmenin sınırlarını fark etmek.

Geçen Haftadan Bu Haftaya Köprü

Geçen hafta kuyruğun mantığını ve enqueue işlemini gördük.

Bu hafta odak noktamız şudur:

Kuyruktan güvenli şekilde eleman çıkarmak ve kuyruğun durumunu doğru takip etmek.

  • ekleme sonda yapılır
  • çıkarma baştan yapılır
  • en kritik hata, son eleman çıkarıldıktan sonra tail değerini unutmak olur

Bağlı Liste Yapımız (Hatırlatma)

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

// Düğüm yapısı
struct Node {
    int veri;
    struct Node *sonraki;
};

struct Node *head = NULL; // Kuyruğun başı (çıkarılacak eleman)
struct Node *tail = NULL; // Kuyruğun sonu (eklenen eleman)

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);
}

Dequeue İşlemi (Kuyruktan Eleman Çıkarma)

  • Kuyruğun başından (head) bir eleman çıkarır.
  • Çıkarılan elemanın verisini döndürür.
  • Kuyruk boşsa önce bunu kontrol etmek zorundayız.
  • Bu örnekte hata durumu için INT_MIN kullanacağız.

dequeue Adım Adım

Başlangıç durumu:

head                  tail
 |                     |
 v                     v
[10] -> [20] -> [30] -> NULL

dequeue() sonrası:

head           tail
 |              |
 v              v
[20] -> [30] -> NULL

Çıkan eleman 10 olur. Yani kuyrukta çıkarma işlemi her zaman baştan yapılır.

dequeue Fonksiyonunda Mantık

  1. Kuyruk boş mu kontrol et.
  2. Eski head düğümünü geçici işaretçide tut.
  3. Çıkarılacak veriyi sakla.
  4. head işaretçisini bir sonraki düğüme kaydır.
  5. Kuyruk boşaldıysa tail = NULL yap.
  6. Eski düğümü free ile serbest bırak.
  7. Çıkarılan veriyi döndür.

Dequeue Fonksiyonu

int dequeue() {
    if (head == NULL) {
        printf("Hata: Kuyruk boş!\n");
        return INT_MIN;
    }

    struct Node *temp = head;
    int cikarilanVeri = temp->veri;

    head = head->sonraki;

    if (head == NULL) {
        tail = NULL;
    }

    free(temp);

    printf("%d kuyruktan çıkarıldı.\n", cikarilanVeri);
    return cikarilanVeri;
}

Tek Elemanlı Durum Neden Özel?

Tek elemanlı kuyrukta hem head hem tail aynı düğümü gösterir:

head
tail
 |
 v
[42] -> NULL

Bu elemanı dequeue() ile çıkarırsak kuyruk tamamen boşalır.

Bu yüzden yalnızca head = NULL yapmak yetmez; tail = NULL da yapılmalıdır.

Peek İşlemi (Öndeki Elemana Bakma)

  • Kuyruğun başındaki (head) elemanın verisini, elemanı çıkarmadan döndürür.
  • Kuyruk boşsa, özel bir değer döndürür (örn: INT_MIN) veya hata mesajı verir.
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;
}

Küçük Tasarım Notu

Bu örneklerde peek() ve dequeue() içinde printf kullandık.

Bu, derste ne olduğunu daha rahat görmek için yararlı.

Ama gerçek programlarda veri yapısı fonksiyonları çoğu zaman sadece işlem yapar ve değer döndürür.

Ekrana yazdırma işi ise genellikle main gibi istemci kodda yapılır.

IsEmpty İşlemi (Kuyruk Boş mu?)

  • Kuyruğun boş olup olmadığını kontrol eder.
  • Boşsa 1 (doğru), değilse 0 (yanlış) döndürür.
int isEmpty() {
    return head == NULL;
}

Bu yapı doğru yönetiliyorsa kuyruk boş olduğunda tail de zaten NULL olur.

Ama boşluk kontrolü için head == NULL yazmak yeterlidir.

INT_MIN Yaklaşımı

dequeue() fonksiyonu normalde bir int değer döndürür.

Ama kuyruk boşsa, gerçekte döndürülecek bir eleman yoktur.

Bu durumda örnek olması için özel bir değer seçiyoruz: INT_MIN.

Bu yaklaşımın mantığı şudur:

  • normal durumda gerçek veri döner,
  • hata durumunda özel bir değer döner.

Bu yöntem derste kullanışlıdır; ama bir sınırı vardır:

Eğer kuyrukta gerçekten INT_MIN değeri varsa, program şunu ayırt edemez:

  • bu bir hata mı,
  • yoksa gerçekten kuyruktan çıkan veri mi?

Yani yöntem basittir; ama kusursuz değildir.

Daha Güvenli Tasarım Fikri

Bu karışıklığı önlemek için bazı programlarda veri ile hata bilgisi ayrı tutulur:

int 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;
}

Bu örnekte sonuc için geçerli bir adres verildiğini varsayıyoruz.

Burada:

  • return 1 -> işlem başarılı,
  • return 0 -> kuyruk boş,
  • *sonuc -> gerçekten çıkarılan veri.

Böylece hata durumu ile gerçek veri birbirine karışmaz.

Örnek Senaryo

İşlemler:

  1. enqueue(10)
  2. enqueue(20)
  3. enqueue(30)
  4. peek()
  5. dequeue()
  6. peek()
  7. dequeue()
  8. enqueue(40)

Öğrencinin burada takip etmesi gereken şey değerler değil, yapının nasıl değiştiğidir.

Örnek Kullanım (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;
}

Beklenen Çıktıyı Yorumlama

İşlem Kuyruğun durumu
başlangıç boş
enqueue(10) 10
enqueue(20) 10 -> 20
enqueue(30) 10 -> 20 -> 30
dequeue() 20 -> 30
enqueue(40) 20 -> 30 -> 40

Bu tablo şunu görünür hale getirir:

  • peek() yapıyı değiştirmez,
  • dequeue() baştaki elemanı çıkarır,
  • işlemler sonunda kuyruğun durumu adım adım takip edilebilir.

Sık Yapılan Hatalar

  • dequeue sonrası eski düğümü free etmemek,
  • tek elemanlı durumda tail = NULL güncellemesini unutmak,
  • peek ile dequeue işlemini aynı şey sanmak,
  • INT_MIN yaklaşımını her durumda güvenli zannetmek,
  • dizi tabanlı kuyrukla bağlı liste tabanlı kuyruğu karıştırmak.

Örnek kod:

https://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.

Hızlı Quiz

  1. peek() işlemi hangisini yapar?
  1. Kuyruğun sonundaki elemanı siler
  2. Kuyruğun başındaki elemanı siler
  3. Kuyruğun başındaki elemanı silmeden gösterir
  4. Kuyruğu tamamen boşaltır

Hızlı Quiz

  1. Bağlı liste ile kuyrukta son eleman çıkarıldığında hangisi doğru olmalıdır?
  1. Sadece head = NULL
  2. Sadece tail = NULL
  3. head = NULL ve tail = NULL
  4. head = tail

Hızlı Quiz

  1. Aşağıdaki işlem sırası uygulandıktan sonra, peek() hangi değeri döndürür?

  2. enqueue(10)

  3. enqueue(20)

  4. dequeue()

  5. enqueue(30)

  6. peek()

  1. 10
  2. 20
  3. 30
  4. Kuyruk boştur

Alıştırma / Tartışma

  1. dequeue fonksiyonu boş kuyruk durumunda INT_MIN döndürmek yerine void olsa ve sadece hata mesajı bassa nasıl olurdu? Avantaj/Dezavantajları nelerdir?
  2. Dairesel Kuyruk (Circular Queue) dizi ile nasıl implemente edilir? Avantajları nelerdir?

Teşekkürler