Kuyruk Veri Yapısı Açıklandı

Kuyruk, İlk Giren İlk Çıkar (FIFO) prensibini takip eden doğrusal bir veri yapısıdır. Bu, kuyruğa ilk eklenen öğenin, kuyruktan ilk kaldırılan öğe olduğu anlamına gelir.

Kuyruklar, bilgisayar bilimlerinde ve yazılım mühendisliğinde çok önemlidir. Görev zamanlama, yazıcı kuyrukları, mesaj kuyrukları, istek işleme, genişlik öncelikli arama (BFS), arka plan işleri, işletim sistemleri ve birçok gerçek yazılım sisteminde kullanılırlar.

Giriş

Veri yapıları, geliştiricilerin verileri nasıl erişileceği ve işleneceği doğrultusunda düzenlemelerine yardımcı olur. Diziler, bağlı listeler, yığınlar, kuyruklar, ağaçlar, grafikler ve hash tabloları farklı erişim modellerini destekler.

Kuyruk, bekleme hatlarını modellediği için en pratik veri yapılarından biridir. Kuyruğa ilk giren öğe, ilk ayrılan öğe olmalıdır. Bu davranış, gerçek hayatta ve yazılım sistemlerinde yaygındır.

Örneğin, bir hizmet masasında bekleyen müşteriler genellikle geliş sırasına göre hizmet alırlar. Bir yazıcı, belgeleri gönderildikleri sıraya göre işler. Bir sunucu, istekleri geldikleri sıraya göre işleyebilir. Bunların hepsi kuyruk benzeri davranışlardır.

Kuyruk Nedir?

Kuyruk, öğelerin bir uçtan eklendiği ve diğer uçtan kaldırıldığı bir öğe koleksiyonudur.

Öğelerin eklendiği uca genellikle kuyruğun arkası (rear) denir. Öğelerin kaldırıldığı uca ise kuyruğun önü (front) denir.

Kuyruk, FIFO kuralını takip eder:

First In, First Out

Bu, en uzun süre beklemiş öğenin ilk işleneceği anlamına gelir.

Ana fikir basittir:

  • Yeni öğeleri arkadan ekleyin.

  • Öğeleri önden kaldırın.

  • Gerektiğinde ön öğeyi kaldırmadan okuyun.

  • Bir öğeyi kaldırmadan önce kuyruğun boş olup olmadığını kontrol edin.

Kuyruk, sıralı işleme için tasarlanmıştır, rastgele erişim için değil.

Kuyruğun Gerçek Hayat Benzetmesi

Kuyruğun yaygın bir gerçek hayat örneği, bir kasada bekleyen insanlardır.

Front → Person A → Person B → Person C ← Rear

A Kişisi ilk geldi, bu yüzden A Kişisi ilk hizmet alır. C Kişisi son geldi, bu yüzden C Kişisi diğerlerinin arkasında bekler.

Yeni bir kişi gelirse, arkadan katılır:

Front → Person A → Person B → Person C → Person D ← Rear

Kasiyer birine hizmet verdiğinde, A Kişisi ilk ayrılır:

Front → Person B → Person C → Person D ← Rear

Programlamada bir Kuyruk tam olarak böyle çalışır.

FIFO Prensibi

FIFO, İlk Giren İlk Çıkar anlamına gelir. Bu, kuyruğa ilk eklenen öğenin, kuyruktan ilk kaldırılan öğe olduğu anlamına gelir.

Örneğin, değerleri bu sırayla kuyruğa eklersek (enqueue):

10, 20, 30

Kuyruk şöyle görünür:

Front → 10 → 20 → 30 ← Rear

Bir öğeyi kuyruktan çıkarırsak (dequeue), 10 değeri ilk kaldırılır çünkü kuyruğa ilk giren oydu.

Bu davranış, Son Giren İlk Çıkar (LIFO) prensibini takip eden Yığın'dan (Stack) farklıdır.

Temel Kuyruk İşlemleri

Bir Kuyruk genellikle küçük bir grup önemli işlemi destekler.

  • enqueue (kuyruğa ekle): Kuyruğun arkasına bir öğe ekler.

  • dequeue (kuyruktan çıkar): Ön öğeyi kaldırır ve döndürür.

  • front (ön): Ön öğeyi kaldırmadan döndürür.

  • rear (arka): Arka öğeyi kaldırmadan döndürür.

  • isEmpty (boş mu): Kuyrukta hiç öğe olup olmadığını kontrol eder.

  • size (boyut): Kuyruktaki öğe sayısını döndürür.

Bu işlemler, öğelerin geliş sırasına göre işlenmesi gerektiğinde Kuyrukları kullanışlı hale getirir.

Enqueue İşlemi

Enqueue işlemi, kuyruğun arkasına yeni bir öğe ekler.

Kuyruk boşsa ve 10'u kuyruğa eklersek, kuyruk şöyle olur:

Front → 10 ← Rear

20'yi kuyruğa eklersek, kuyruk şöyle olur:

Front → 10 → 20 ← Rear

30'u kuyruğa eklersek, kuyruk şöyle olur:

Front → 10 → 20 → 30 ← Rear

Her yeni öğe, mevcut arka öğeden sonra eklenir.

Dequeue İşlemi

Dequeue işlemi, kuyruğun önündeki öğeyi kaldırır ve döndürür.

Şu kuyruğu ele alalım:

Front → 10 → 20 → 30 ← Rear

Dequeue'u çağırırsak, 10 değeri kaldırılır.

Removed: 10

Kuyruk şöyle olur:

Front → 20 → 30 ← Rear

Tekrar dequeue çağırırsak, 20 değeri kaldırılır.

Removed: 20

Kuyruk şöyle olur:

Front → 30 ← Rear

Dequeue işlemi, kuyruk boşken dikkatli kullanılmalıdır. Boş bir kuyruktan kaldırma işlemine genellikle kuyruk taşması (queue underflow) denir.

Front İşlemi

Front işlemi, kuyruktaki ilk öğeyi kaldırmadan döndürür.

Şu kuyruğu ele alalım:

Front → 10 → 20 → 30 ← Rear

Front'u çağırırsak, sonuç şudur:

10

Kuyruk değişmeden kalır:

Front → 10 → 20 → 30 ← Rear

Bu işlem, işlenecek bir sonraki öğeyi incelememiz gerektiğinde kullanışlıdır.

Rear İşlemi

Rear işlemi, kuyruktaki son öğeyi kaldırmadan döndürür.

Şu kuyruğu ele alalım:

Front → 10 → 20 → 30 ← Rear

Rear'ı çağırırsak, sonuç şudur:

30

Kuyruk değişmeden kalır.

Rear, en son eklenen öğeyi incelememiz gerektiğinde kullanışlıdır.

isEmpty İşlemi

isEmpty işlemi, kuyrukta herhangi bir öğe olup olmadığını kontrol eder.

Kuyrukta hiç öğe yoksa:

[]

Bu durumda isEmpty, true döndürür.

Kuyruk öğeler içeriyorsa:

[10, 20, 30]

Bu durumda isEmpty, false döndürür.

Bu işlem, dequeue, front veya rear çağırmadan önce önemlidir.

Manuel Çalıştırma Örneği

Kuyruk işlemlerinin bir dizisini manuel olarak çalıştıralım.

enqueue(10)
enqueue(20)
enqueue(30)
front()
dequeue()
enqueue(40)
dequeue()
rear()

Boş bir kuyrukla başlıyoruz:

Queue: []

Adım 1: enqueue(10)

10'u kuyruğun arkasına ekleriz.

Front → 10 ← Rear

Adım 2: enqueue(20)

20'yi 10'dan sonra ekleriz.

Front → 10 → 20 ← Rear

Adım 3: enqueue(30)

30'u 20'den sonra ekleriz.

Front → 10 → 20 → 30 ← Rear

Adım 4: front()

Ön öğe 10'dur.

front() returns 10

Kuyruk değişmez:

Front → 10 → 20 → 30 ← Rear

Adım 5: dequeue()

Ön öğe 10 kaldırılır.

dequeue() returns 10

Kuyruk şöyle olur:

Front → 20 → 30 ← Rear

Adım 6: enqueue(40)

40'ı arkaya ekleriz.

Front → 20 → 30 → 40 ← Rear

Adım 7: dequeue()

Ön öğe 20 kaldırılır.

dequeue() returns 20

Kuyruk şöyle olur:

Front → 30 → 40 ← Rear

Adım 8: rear()

Arka öğe 40'tır.

rear() returns 40

Kuyruk değişmeden kalır:

Front → 30 → 40 ← Rear

Bu manuel çalıştırma, FIFO davranışını açıkça gösterir. İlk eklenen değer, ilk kaldırılan değerdir.

Dizi Kullanarak Kuyruk Uygulaması

Bir Kuyruk, bir dizi kullanılarak uygulanabilir. En basit yaklaşım, öğeleri dizinin sonuna eklemek ve baştan kaldırmaktır.

Örneğin:

[10, 20, 30]

10 değeri ön, 30 değeri ise arkadır.

Ancak, bir dizinin başından kaldırma işlemi bazı dillerde verimsiz olabilir çünkü kalan öğelerin kaydırılması gerekebilir. Daha iyi bir uygulama, her dequeue işleminde kaydırmayı önlemek için bir ön indeks kullanır.

Kuyruk Sözde Kodu

class Queue:
    items = empty array
    frontIndex = 0

    enqueue(value):
        add value to end of items

    dequeue():
        if queue is empty:
            return error

        value = items[frontIndex]
        frontIndex = frontIndex + 1
        return value

    front():
        if queue is empty:
            return error

        return items[frontIndex]

    isEmpty():
        return frontIndex >= length of items

    size():
        return length of items - frontIndex

Bu sürüm, öğeleri doğrudan dizinin başından kaldırmayı önler.

PHP'de Kuyruk Örneği

İşte bir dizi ve bir ön indeks kullanarak Kuyruğun temiz bir PHP uygulaması.

<?php

class Queue
{
    private array $items = [];
    private int $frontIndex = 0;

    public function enqueue(mixed $value): void
    {
        $this->items[] = $value;
    }

    public function dequeue(): mixed
    {
        if ($this->isEmpty()) {
            throw new RuntimeException('Cannot dequeue from an empty queue.');
        }

        $value = $this->items[$this->frontIndex];
        $this->frontIndex++;

        return $value;
    }

    public function front(): mixed
    {
        if ($this->isEmpty()) {
            throw new RuntimeException('Cannot read front from an empty queue.');
        }

        return $this->items[$this->frontIndex];
    }

    public function rear(): mixed
    {
        if ($this->isEmpty()) {
            throw new RuntimeException('Cannot read rear from an empty queue.');
        }

        return $this->items[count($this->items) - 1];
    }

    public function isEmpty(): bool
    {
        return $this->frontIndex >= count($this->items);
    }

    public function size(): int
    {
        return count($this->items) - $this->frontIndex;
    }
}

$queue = new Queue();

$queue->enqueue(10);
$queue->enqueue(20);
$queue->enqueue(30);

echo $queue->front(); // 10
echo PHP_EOL;

echo $queue->dequeue(); // 10
echo PHP_EOL;

$queue->enqueue(40);

echo $queue->rear(); // 40

Bu uygulama, büyük diziler için verimsiz olabilen array_shift kullanmaktan kaçınır çünkü dahili olarak öğeleri kaydırabilir.

JavaScript'te Kuyruk Örneği

İşte bir dizi ve bir ön indeks kullanan bir JavaScript uygulaması.

class Queue {
    constructor() {
        this.items = [];
        this.frontIndex = 0;
    }

    enqueue(value) {
        this.items.push(value);
    }

    dequeue() {
        if (this.isEmpty()) {
            throw new Error('Cannot dequeue from an empty queue.');
        }

        const value = this.items[this.frontIndex];
        this.frontIndex++;

        return value;
    }

    front() {
        if (this.isEmpty()) {
            throw new Error('Cannot read front from an empty queue.');
        }

        return this.items[this.frontIndex];
    }

    rear() {
        if (this.isEmpty()) {
            throw new Error('Cannot read rear from an empty queue.');
        }

        return this.items[this.items.length - 1];
    }

    isEmpty() {
        return this.frontIndex >= this.items.length;
    }

    size() {
        return this.items.length - this.frontIndex;
    }
}

const queue = new Queue();

queue.enqueue(10);
queue.enqueue(20);
queue.enqueue(30);

console.log(queue.front());   // 10
console.log(queue.dequeue()); // 10

queue.enqueue(40);

console.log(queue.rear());    // 40

Bu JavaScript sürümü, her dequeue için shift() kullanmaktan kaçınır. shift() kullanmak daha az verimli olabilir çünkü tüm kalan öğeleri hareket ettirebilir.

Bağlı Liste Kullanarak Kuyruk Uygulaması

Bir Kuyruk, Bağlı Liste kullanılarak da uygulanabilir. Bu uygulamada, kuyruk hem ön referansını hem de arka referansını korur.

Ön, bir sonraki kaldırılacak düğümü işaret eder. Arka ise yeni öğelerin eklendiği düğümü işaret eder.

Front → [10] → [20] → [30] ← Rear

Hem ön hem de arka referanslarıyla, enqueue ve dequeue işlemleri O(1) olabilir.

JavaScript'te Bağlı Liste Kuyruk Örneği

class QueueNode {
    constructor(data) {
        this.data = data;
        this.next = null;
    }
}

class LinkedListQueue {
    constructor() {
        this.frontNode = null;
        this.rearNode = null;
        this.length = 0;
    }

    enqueue(value) {
        const newNode = new QueueNode(value);

        if (this.rearNode === null) {
            this.frontNode = newNode;
            this.rearNode = newNode;
        } else {
            this.rearNode.next = newNode;
            this.rearNode = newNode;
        }

        this.length++;
    }

    dequeue() {
        if (this.isEmpty()) {
            throw new Error('Cannot dequeue from an empty queue.');
        }

        const removedValue = this.frontNode.data;
        this.frontNode = this.frontNode.next;

        if (this.frontNode === null) {
            this.rearNode = null;
        }

        this.length--;

        return removedValue;
    }

    front() {
        if (this.isEmpty()) {
            throw new Error('Cannot read front from an empty queue.');
        }

        return this.frontNode.data;
    }

    rear() {
        if (this.isEmpty()) {
            throw new Error('Cannot read rear from an empty queue.');
        }

        return this.rearNode.data;
    }

    isEmpty() {
        return this.frontNode === null;
    }

    size() {
        return this.length;
    }
}

Bu bağlı liste tabanlı uygulama, kaydırma veya indeks hareketi gerektirmediği için verimlidir.

Dizi Kuyruğu ve Bağlı Liste Kuyruğu Karşılaştırması

Hem diziler hem de bağlı listeler bir Kuyruk uygulamak için kullanılabilir.

Dizi tabanlı bir kuyruk, özellikle üst düzey dillerde basit ve kullanışlıdır. Bağlı liste tabanlı bir kuyruk ise öğeleri kaydırma endişesi duymadan verimli dinamik ekleme ve çıkarma istediğimizde kullanışlıdır.

Başlıca farklılıklar şunlardır:

  • Dizi kuyruğunun uygulanması kolaydır.

  • Bağlı liste kuyruğu düğümler ve referanslar kullanır.

  • Dizi kuyruğu, maliyetli ön kaldırma işlemlerinden kaçınmalıdır.

  • Bağlı liste kuyruğu, ön ve arka referanslarla O(1) enqueue ve dequeue işlemlerini destekleyebilir.

  • Dizi kuyruğu, ön indeks sürekli artarsa kullanılmayan alan bırakabilir.

  • Bağlı liste kuyruğu, düğüm referansları için ekstra bellek kullanır.

Öğrenmek için her iki uygulama da kullanışlıdır. Üretim için ise yerleşik kuyruk yapıları veya optimize edilmiş kütüphaneler daha iyi olabilir.

Kuyruk Türleri

Birçok Kuyruk türü vardır. Her tür, farklı bir problemi çözer.

Başlıca türler şunlardır:

  • Basit Kuyruk.

  • Dairesel Kuyruk.

  • Öncelikli Kuyruk.

  • Çift Uçlu Kuyruk (Deque).

Bu türleri anlamak, geliştiricilerin farklı durumlar için doğru kuyruk yapısını seçmelerine yardımcı olur.

Basit Kuyruk

Basit Kuyruk, normal FIFO kuralını takip eder. Öğeler arkadan eklenir ve önden kaldırılır.

Front → 10 → 20 → 30 ← Rear

Bu, çoğu başlangıç örneğinde kullanılan standart kuyruktur.

Basit Kuyruklar şunlar için kullanışlıdır:

  • Temel görev işleme.

  • İstek işleme.

  • Sıraya dayalı işleme.

  • FIFO davranışını öğretme.

Dairesel Kuyruk

Dairesel Kuyruk, kuyruğun sonunu mantıksal olarak başına bağlar. Genellikle sabit boyutlu bir dizi kullanılarak uygulanır.

Ana fikir, arka dizinin sonuna ulaştığında, boş alan varsa başına dönebilmesidir (wrap around).

Array size: 5

[_, 20, 30, 40, _]
 front      rear

Başlangıçta yer varsa, arka oraya geçebilir:

[50, 20, 30, 40, _]
 rear front

Dairesel Kuyruklar, sabit boyutlu tamponlar, akış verileri ve işletim sistemi zamanlaması ile çalışırken kullanışlıdır.

Öncelikli Kuyruk

Öncelikli Kuyruk, öğeleri geliş sırasına göre değil, önceliğe göre kaldırır.

Örneğin, bir hastane acil durum sisteminde, kritik bir hasta, daha önce gelmiş ancak durumu daha az acil olan birinden önce tedavi edilebilir.

Task A: priority 2
Task B: priority 5
Task C: priority 1

Daha yüksek öncelikli değerler önce işlenirse, Görev B, Görev A ve Görev C'den önce kaldırılır.

Öncelikli Kuyruklar genellikle heap (yığın) kullanılarak uygulanır, çünkü heap'ler en yüksek veya en düşük öncelikli öğenin verimli bir şekilde eklenmesini ve kaldırılmasını sağlar.

Çift Uçlu Kuyruk (Deque)

Deque (çift uçlu kuyruk olarak telaffuz edilir), her iki uçtan da ekleme ve kaldırma işlemlerine izin verir.

Front ⇄ [10] ⇄ [20] ⇄ [30] ⇄ Rear

Bir Deque, nasıl kullanıldığına bağlı olarak bir Kuyruk veya bir Yığın gibi davranabilir.

Deque işlemleri şunları içerir:

  • Önden ekle.

  • Arkadan ekle.

  • Önden kaldır.

  • Arkadan kaldır.

Deque'ler, kayan pencere algoritmalarında, geri al-yinele sistemlerinde ve gelişmiş zamanlama problemlerinde kullanışlıdır.

Kuyruk İşlemlerinin Zaman Karmaşıklığı

Zaman karmaşıklığı, bir işlemin çalışma süresinin öğe sayısı arttıkça nasıl büyüdüğünü açıklar.

İyi bir uygulamaya sahip standart bir Kuyruk için yaygın karmaşıklıklar şunlardır:

  • enqueue: O(1)

  • dequeue: O(1)

  • front: O(1)

  • rear: O(1)

  • isEmpty: O(1)

  • size: O(1)

  • search (arama): O(n)

Kuyruk doğru uygulandığında enqueue ve dequeue sabit zamanlı işlemlerdir.

Enqueue Neden O(1)dir?

Enqueue işlemi, kuyruğun arkasına yeni bir öğe ekler.

Before:
Front → 10 → 20 ← Rear

enqueue(30)

After:
Front → 10 → 20 → 30 ← Rear

Arka konum biliniyorsa, yeni öğeyi eklemek tüm kuyruğu taramayı gerektirmez.

Bu nedenle, enqueue:

O(1)

Dequeue Neden O(1)dir?

Dequeue işlemi, ön öğeyi kaldırır.

Before:
Front → 10 → 20 → 30 ← Rear

dequeue()

After:
Front → 20 → 30 ← Rear

Ön konum biliniyorsa, ön öğeyi kaldırma doğrudan yapılabilir.

Bu nedenle, dequeue:

O(1)

Bu, iyi bir uygulama olduğunu varsayar. Bir dizi uygulaması her seferinde maliyetli bir kaydırma işlemi kullanıyorsa, dequeue O(n) haline gelebilir.

Arama Neden O(n)dir?

Bir Kuyruk içinde arama yapmak yapının ana amacı değildir. Belirli bir değeri bulmamız gerekirse, öğeleri tek tek incelememiz gerekebilir.

Front → 10 → 20 → 30 ← Rear

30'u aramak için önce 10'u, sonra 20'yi, sonra 30'u kontrol etmemiz gerekebilir.

En kötü durumda, tüm öğeler kontrol edilir, bu nedenle arama:

O(n)

Kuyruğun Uzay Karmaşıklığı

Bir Kuyruğun uzay karmaşıklığı O(n)'dir, burada n kuyrukta depolanan öğe sayısıdır.

Kuyruk 100 öğe depoluyorsa, 100 öğe için belleğe ihtiyaç duyar. 1.000 öğe depoluyorsa, 1.000 öğe için belleğe ihtiyaç duyar.

Space complexity = O(n)

Kuyruk bağlı düğümler kullanılarak uygulanıyorsa, her düğüm bir sonraki düğüme bir referans da depolar. Bir dizi kullanılarak uygulanıyorsa, bellek dizi kapasitesine ve uygulama stratejisine bağlıdır.

Kuyruk Taşması ve Kuyruk Altı Taşması

Kuyruk taşması (queue overflow), bir kuyruğun sabit bir kapasitesi olduğunda ve bir programın kuyruğun tutabileceğinden daha fazla öğe eklemeye çalıştığında meydana gelir.

Queue capacity: 3
Queue: [10, 20, 30]

enqueue(40) → overflow

Kuyruk altı taşması (queue underflow), bir programın boş bir kuyruktan bir öğeyi kaldırmaya veya okumaya çalıştığında meydana gelir.

Queue: []

dequeue() → underflow

İyi Kuyruk uygulamaları, bir istisna fırlatarak, null döndürerek veya açık bir hata stratejisi kullanarak bu durumları güvenli bir şekilde ele almalıdır.

Kuyruklar ve Genişlik Öncelikli Arama (BFS)

Bir Kuyruğun en önemli algoritmik kullanımlarından biri Genişlik Öncelikli Arama (Breadth-First Search) veya bilinen adıyla BFS'tir.

BFS, düğümleri seviye seviye keşfeder. Bir Kuyruk, hangi düğümün bir sonraki işleneceğini hatırlamak için kullanılır.

Örneğin, bir grafikte veya ağaçta:

Start with node A
enqueue(A)

While queue is not empty:
    dequeue a node
    visit it
    enqueue its unvisited neighbors

FIFO davranışı, daha önce keşfedilen düğümlerin daha erken işlenmesini sağlar ve bu da seviye sırasına göre geçiş (level-order traversal) oluşturur.

Kuyruklar ve Görev Zamanlama

Kuyruklar, görevlerin geliş sırasına göre işlenmesi gereken görev zamanlama sistemlerinde kullanılır.

Örneğin:

Task 1: Send email
Task 2: Generate report
Task 3: Resize image

Bir çalışan, kuyruğun önündeki görevleri işleyebilir:

dequeue() → Send email
dequeue() → Generate report
dequeue() → Resize image

Bu model, arka plan iş sistemlerinde ve mesaj aracıları (message brokers) arasında yaygındır.

Kuyruklar ve Yazıcı İşleri

Bir yazıcı kuyruğu, klasik bir Kuyruk örneğidir. Üç belge bir yazıcıya gönderilirse, genellikle geldikleri sıraya göre yazdırılırlar.

Front → Document A → Document B → Document C ← Rear

Belge A önce, sonra Belge B, sonra Belge C yazdırılır.

Bu, gerçek bir sistemde FIFO davranışıdır.

Kuyruklar ve Web İstekleri

Kuyruklar web sistemlerinde de kullanılır. Çok sayıda istek veya arka plan işi geldiğinde, bir kuyruk bunları çalışanlar işlemeye hazır olana kadar tutabilir.

Örneğin, bir Laravel uygulaması kuyrukları şunlar için kullanabilir:

  • E-posta gönderme.

  • Yüklenen dosyaları işleme.

  • Rapor oluşturma.

  • Bildirim gönderme.

  • Yavaş görevleri arka planda çalıştırma.

Bu, yavaş işlemlerin ana kullanıcı isteğini engellemesini önler.

Pratik Örnek: JavaScript'te Basit Görev Kuyruğu

İşte basit bir görev kuyruğu örneği:

class TaskQueue {
    constructor() {
        this.queue = [];
        this.frontIndex = 0;
    }

    addTask(task) {
        this.queue.push(task);
    }

    processTask() {
        if (this.frontIndex >= this.queue.length) {
            return null;
        }

        const task = this.queue[this.frontIndex];
        this.frontIndex++;

        return task;
    }
}

const tasks = new TaskQueue();

tasks.addTask('Send email');
tasks.addTask('Generate report');
tasks.addTask('Resize image');

console.log(tasks.processTask()); // Send email
console.log(tasks.processTask()); // Generate report

Eklenen ilk görev, işlenen ilk görevdir.

Kuyruk ve Yığın Karşılaştırması

Kuyruklar ve Yığınlar her ikisi de doğrusal veri yapılarıdır, ancak farklı erişim kurallarını takip ederler.

Bir Kuyruk FIFO'yu takip eder:

First In, First Out

Bir Yığın LIFO'yu takip eder:

Last In, First Out

Örneğin, bir Kuyruk bekleyen bir insan sırası gibidir. İlk gelen kişi ilk hizmet alır.

Bir Yığın, bir tabak yığını gibidir. Üste konulan son tabak ilk kaldırılır.

Başlıca farklılıklar şunlardır:

  • Kuyruk en eski öğeyi önce kaldırır.

  • Yığın en yeni öğeyi önce kaldırır.

  • Kuyruk enqueue ve dequeue kullanır.

  • Yığın push ve pop kullanır.

  • Kuyruk, zamanlama ve bekleme sıraları için kullanışlıdır.

  • Yığın, geri alma (undo), özyineleme (recursion) ve geri izleme (backtracking) için kullanışlıdır.

Geliştiriciler Kuyruğu Ne Zaman Kullanmalı?

Geliştiriciler, öğelerin geldikleri sırayla işlenmesi gerektiğinde bir Kuyruk kullanmalıdır.

Kuyruklar şunlar için kullanışlıdır:

  • İlk giren, ilk çıkar davranışı gereklidir.

  • Görevler geliş sırasına göre işlenmelidir.

  • İsteklerin işlenmeden önce beklemesi gerekir.

  • Genişlik öncelikli arama (BFS) gereklidir.

  • Arka plan işleri kullanılır.

  • Mesaj işleme gereklidir.

  • Zamanlama veya tamponlama gereklidir.

Ana sinyal FIFO davranışıdır. En eski öğe önce işlenmeli ise, bir Kuyruk doğru yapı olabilir.

Kuyruk Ne Zaman Kullanılmamalıdır?

En yeni öğe önce işlenmeli veya rastgele erişim gerektiğinde bir Kuyruk doğru yapı değildir.

Geliştiriciler Kuyruktan şunlar olduğunda kaçınmalıdır:

  • Son giren, ilk çıkar davranışı gereklidir.

  • İndekse göre hızlı rastgele erişim gereklidir.

  • Öncelik tabanlı işleme gereklidir.

  • Sık sık arama yapmak ana işlemdir.

  • Geliş sırası önemli değildir.

Bu durumlarda, bir Yığın, Dizi, Hash Tablosu, Öncelikli Kuyruk veya başka bir veri yapısı daha uygun olabilir.

Kuyrukları Öğrenirken Sık Yapılan Hatalar

Yeni başlayanlar genellikle FIFO fikrini anlarlar ancak uygulama veya karmaşıklık analizinde hata yaparlar.

Sık yapılan hatalar şunlardır:

  • Kuyruğu Yığın ile karıştırmak.

  • Yanlış uçtan kaldırma.

  • Performansı anlamadan maliyetli dizi kaydırma kullanmak.

  • Boş kuyruk durumlarını ele almayı unutmak.

  • Aramanın O(1) olduğunu varsaymak.

  • Bağlı liste kuyruklarında ön ve arkayı doğru şekilde güncellememek.

  • Dairesel kuyruklarda yanlış döngü (wrap-around) mantığı oluşturmak.

En güvenli kural basittir: arkadan kuyruğa ekle ve önden kuyruktan çıkar.

Kuyrukları Anlamak İçin Pratik Kontrol Listesi

Gelişmiş veri yapılarına ve grafik algoritmalarına geçmeden önce, geliştiriciler şu soruları yanıtlayabilmelidir:

  • FIFO ne anlama gelir?

  • Bir Kuyruğun önü nedir?

  • Bir Kuyruğun arkası nedir?

  • Enqueue ne işe yarar?

  • Dequeue ne işe yarar?

  • İyi bir uygulamada enqueue ve dequeue neden O(1)'dir?

  • Kuyruk altı taşması (queue underflow) nedir?

  • Kuyruk ile Yığın arasındaki fark nedir?

  • Dairesel Kuyruk nedir?

  • BFS neden bir Kuyruk kullanır?

Bu sorular netse, geliştirici Kuyrukların ardındaki gerçek mantığı anlamıştır.

Kuyrukların Avantajları

Kuyrukların birkaç önemli avantajı vardır.

  • Anlamaları basittir.

  • Doğal olarak FIFO davranışını desteklerler.

  • Zamanlama ve bekleme sıraları için kullanışlıdırlar.

  • Enqueue ve dequeue O(1) olabilir.

  • BFS için temeldirler.

  • Arka plan iş sistemlerinde ve mesaj işlemede kullanışlıdırlar.

Bu avantajlar, Kuyrukları bilgisayar bilimlerindeki en önemli veri yapılarından biri yapar.

Kuyrukların Dezavantajları

Kuyrukların sınırlamaları da vardır.

  • Hızlı rastgele erişim sağlamazlar.

  • Bir Kuyruk içinde arama yapmak O(n)'dir.

  • LIFO davranışı için uygun değildirler.

  • Öncelik tabanlı işleme, bunun yerine bir Öncelikli Kuyruk gerektirir.

  • Dizi tabanlı uygulamalar, verimsiz kaydırmayı önlemek için dikkatlice tasarlanmalıdır.

Bu sınırlamalar normaldir. Sadece Kuyrukların her olası erişim deseni için değil, sıralı işleme için tasarlandığını gösterirler.

Gerçek Yazılım Projelerinde Kuyruklar

Kuyruklar, gerçek yazılım projelerinde birçok biçimde ortaya çıkar. Geliştiriciler, Kuyruk sınıflarını manuel olarak uygulamak yerine yerleşik kuyrukları, mesaj aracılarını, iş sistemlerini veya framework kuyruk araçlarını kullanabilirler.

Yaygın gerçek dünya kullanımları şunlardır:

  • Arka plan işi işleme.

  • E-posta gönderme.

  • Bildirim sistemleri.

  • Yazıcı işi yönetimi.

  • İstek tamponlama.

  • İşletim sistemi zamanlaması.

  • Mesaj kuyrukları.

  • Ağaçlarda ve grafiklerde genişlik öncelikli arama (BFS).

Kuyrukları anlamak, geliştiricilerin eşzamansız ve sıralı işleme sistemlerinin dahili olarak nasıl çalıştığını anlamalarına yardımcı olur.

Sonuç

Kuyruk, İlk Giren İlk Çıkar prensibini takip eden doğrusal bir veri yapısıdır. Kuyruğa ilk eklenen öğe, ilk kaldırılan öğedir.

Başlıca Kuyruk işlemleri enqueue, dequeue, front, rear, isEmpty ve size'dır. Enqueue bir öğeyi arkaya eklerken, dequeue ön öğeyi kaldırır.

İyi bir uygulama ile enqueue, dequeue, front, rear, isEmpty ve size O(1) karmaşıklığındadır. Kuyruk içinde arama yapmak O(n)'dir çünkü yapı rastgele erişim için tasarlanmamıştır.

Kuyruklar, diziler veya bağlı listeler kullanılarak uygulanabilir. Ayrıca Dairesel Kuyruk, Öncelikli Kuyruk ve Çift Uçlu Kuyruk (Deque) gibi önemli varyasyonları da vardır.

Veri Yapıları ve Algoritmaları öğrenen geliştiriciler için Kuyruklar temeldir. FIFO davranışını, sıralı işlemeyi, zamanlama mantığını, BFS geçişini ve arka plan işleri, mesaj kuyrukları ve istek işleme gibi birçok gerçek dünya sisteminin temelini öğretirler.