Bölüm 7: Kuyruklar
Giriş
Öğrenme Çıktıları
Kuyruğu Q = {a0, …, an−1} olarak tanımlayıp ön ve arka uçları göstermek.
Önde boşalan hücrelerin neden kullanılamadığını ve kaydırmanın maliyetini açıklamak.
% MAX ile sarma yapıp dolu/boş ayrımını üç yöntemle çözmek.
on ve arka işaretçileriyle O(1) ekleme/çıkarma yapmak.
İki uçtan ekleme ve çıkarma yapan çift uçlu kuyruğu dairesel dizide gerçeklemek.
Zaman dilimli iş zamanlama benzetiminde kuyruğu kullanmak; iki yığınla kuyruk kurmak.
Kuyruk ADT
Kuyruk (Queue): İlk Giren İlk Çıkar
Q = {a0, a1, …, an−1} kuyruğunda, yığından farklı olarak işlenecek ilk öğe a0'dır (ön, front); an−1 kuyruğun arkasında (rear) yer alır. Ekleme arkadan, silme önden yapılır: FIFO (first-in-first-out).
Boş kuyruk: ön ve arka aynı yerde.
A arkaya eklendi.
B arkaya eklendi.
C arkaya eklendi.
D arkaya eklendi.
İlk alınan, ilk eklenen A olur: FIFO. Öndeki hücre boşaldı.
Kuyruk ADT
Soyut Veri Tipi: Kuyruk
| İşlem | Anlamı | Hata durumu |
|---|---|---|
kuyruk_olustur(max) | En çok max öğelik boş kuyruk | — |
kuyruk_dolu(Q) | Yer kalmadıysa doğru | — |
kuyruk_ekle(Q, x) · enqueue | x'i arkaya ekler | Dolu ise taşma |
kuyruk_bos(Q) | Öğe yoksa doğru | — |
kuyruk_al(Q) · dequeue | Öndeki öğeyi çıkarıp döndürür | Boş ise alt taşma |
kuyruk_on(Q) · peek | Öndekini çıkarmadan okur | Boş ise hata |
Tek uç: ekle ve al üstte. LIFO.
İki uç: ekle arkada, al önde. FIFO.
Kuyruk ADT
Kuyruğun Kullanıldığı Yerler
Çalışmaya hazır süreçler, yazıcı işleri, disk istekleri sırayla kuyrukta bekler.
Klavye girdisi, ağ paketleri, ses akışı: üretici ile tüketici arasında dairesel tampon.
BFS ve ağacın seviye sırası gezinmesi: işlenen düğümün çocukları arkaya eklenir, sıradaki düğüm önden alınır.
Banka gişesi, trafik ışığı, iş zamanlama: gelen müşteri sıraya girer.
Sunucular arasında işler sırayla ve kaybolmadan aktarılır.
“Kuyruk” adını taşır ama FIFO değildir: en öncelikli öğe önce çıkar.
Kuyruk ADT
Arkadan Ekle, Önden Al
Çıkan sıra, giren sırayla aynıdır. Yığında tam tersiydi.
Doğrusal Kuyruk
Dizi ile Doğrusal Kuyruk
#define MAX_QUEUE_SIZE 100 element kuyruk[MAX_QUEUE_SIZE]; int on = -1; /* ilk öğenin bir öncesi */ int arka = -1; /* son öğe */ void kuyruk_ekle(int *arka, element item) { if (*arka >= MAX_QUEUE_SIZE - 1) /* yer var mı? */ kuyruk_dolu(); /* uyar ve çık */ kuyruk[++*arka] = item; /* önce artır, sonra yaz */ } element kuyruk_al(int *on, int arka) { if (*on == arka) kuyruk_bos(); /* boşsa uyar ve çık */ return kuyruk[++*on]; /* artır ve öndekini döndür */ }
onilk öğeyi değil, bir öncesini gösterir;arkason öğeyi gösterir.- Boş: on == arka (başta ikisi de −1).
- Dolu: arka == MAX − 1, önde boş yer olsa bile.
kuyruk_alarka'yı değiştirmez; bu yüzden onu değerle alır.kuyruk_dolu/kuyruk_bos: yığındaki gibi mesaj +exit.
Doğrusal Kuyruk · Kodu adım adım çalıştır
Kod İzleme: Doğrusal Kuyruk ve “Sahte Doluluk”
Başta on = arka = −1: kuyruk boş. MAX_QUEUE_SIZE = 4. Sırayla ekle A, B, C; iki kez al; ekle D, E.
-1 ≥ 3? Hayır.
++*arka → 0; kuyruk[0] = A.
0 ≥ 3? Hayır.
++*arka → 1; kuyruk[1] = B.
1 ≥ 3? Hayır.
++*arka → 2; kuyruk[2] = C.
on == arka (-1 == 2)? Hayır, boş değil.
++*on → 0; kuyruk[0] = A döner. Hücre artık kullanılmıyor.
on == arka (0 == 2)? Hayır, boş değil.
++*on → 1; kuyruk[1] = B döner. Hücre artık kullanılmıyor.
2 ≥ 3? Hayır.
++*arka → 3; kuyruk[3] = D.
3 ≥ 3 → dolu sayılır! Oysa 0 ve 1 numaralı hücreler boş.
kuyruk_dolu(): program sonlanır. Doğrusal kuyruğun sorunu: arka sona dayanınca öndeki boş yerler kullanılamaz.
void kuyruk_ekle(int *arka, element item) { if (*arka >= MAX_QUEUE_SIZE - 1) kuyruk_dolu(); /* "Kuyruk Dolu!!!", exit */ kuyruk[++*arka] = item; } element kuyruk_al(int *on, int arka) { if (*on == arka) kuyruk_bos(); /* "Kuyruk Bos!!!", exit */ return kuyruk[++*on]; }
Doğrusal Kuyruk
Önde Boşalan Yer: Kaydırmak mı?
Önde iki boş hücre var ama arka sona dayandı.
Kaydırma: n öğenin her biri bir kez taşınır → O(n).
Yer açıldı; ama her “dolu”da bunu yapmak ekleme işlemini O(n) yapar.
Kaydırmalar aşırı zaman alır; çözüm olarak döngüsel (dairesel) kuyruk geliştirilmiştir: kuyruğun başı ile sonu birleştirilir, önde boşalan yerler arkadaymış gibi kullanılır.
Dairesel Kuyruk
Dairesel (Döngüsel) Kuyruk
- Dizinin sonu başına bağlanmış gibi düşünülür: 5'ten sonra 0 gelir.
- İlerleme:
arka = (arka + 1) % MAX,on = (on + 1) % MAX. onyine ilk öğenin bir öncesini gösterir; baştaon = arka = 0.- Boş: on == arka. Dolu: (arka + 1) % MAX == on.
- Bu yüzden MAX hücrenin biri boş kalır: en çok MAX − 1 öğe.
Dairesel Kuyruk
Sarma (wrap-around): % MAX
| arka | arka + 1 | (arka + 1) % 6 | Anlamı |
|---|---|---|---|
| 3 | 4 | 4 | normal ilerleme |
| 4 | 5 | 5 | son hücre |
| 5 | 6 | 0 | sarma: başa dön |
| 0 | 1 | 1 | yeniden normal |
Bir geri adım: (i − 1 + MAX) % MAX. C'de -1 % 6 −1 verir; + MAX eklemek negatif sonucu önler (deque bunu kullanır).
(arka − on + MAX) % MAX. Örnek: on = 4, arka = 1, MAX = 6 → (1 − 4 + 6) % 6 = 3.
Dairesel Kuyruk
Dairesel Kuyruk Şekli: A…F ile Sarma
Boş kuyruk: on == arka = 0.
A eklendi: arka = 1.
B eklendi: arka = 2.
C eklendi: arka = 3.
D eklendi: arka = 4.
E eklendi: arka = 5.
A alındı: on = 1. Artık 0 ve 1 numaralı hücrelerden biri boş.
F, arka = (5 + 1) % 6 = 0'a yazıldı: önde boşalan yer arkadaymış gibi kullanıldı. Kuyruk şimdi dolu: (0 + 1) % 6 == on. 6 hücrede 5 öğe.
Dairesel Kuyruk
Dairesel Kuyruk: C Kodu
void kuyruk_ekle(int on, int *arka, element item) { *arka = (*arka + 1) % MAX_QUEUE_SIZE; /* ilerlet */ if (on == *arka) kuyruk_dolu(); /* dolu mu? */ kuyruk[*arka] = item; /* yerleştir */ } element kuyruk_al(int *on, int arka) { if (*on == arka) kuyruk_bos(); /* boş mu? */ *on = (*on + 1) % MAX_QUEUE_SIZE; /* ilerlet */ return kuyruk[*on]; /* döndür */ }
kuyruk_ekle önce *arka'yı ilerletip sonra doluluğa bakar. kuyruk_dolu programı bitirdiği için sorun olmaz; ama hata döndüren bir sürümde arka bozulur (on'a eşit kalır ve kuyruk boş görünür). Güvenli sürümde yeni konum önce geçici bir değişkende denenir (Horowitz–Sahni de rear'ı geri alır).Dairesel Kuyruk · Kodu adım adım çalıştır
Kod İzleme: Güvenli Dairesel Kuyruk
MAX_QUEUE_SIZE = 5, on = arka = 0. Ekle A, B, C, D, E; al, al; ekle E, F, G.
yeni = (0 + 1) % 5 = 1; 1 ≠ on, yer var.
arka = 1, kuyruk[1] = A.
yeni = (1 + 1) % 5 = 2; 2 ≠ on, yer var.
arka = 2, kuyruk[2] = B.
yeni = (2 + 1) % 5 = 3; 3 ≠ on, yer var.
arka = 3, kuyruk[3] = C.
yeni = (3 + 1) % 5 = 4; 4 ≠ on, yer var.
arka = 4, kuyruk[4] = D.
yeni = (4 + 1) % 5 = 0 == on → dolu, false döner. arka 4 olarak korunur. 5 hücrede 4 öğe.
on == arka (0 == 4)? Hayır.
on = 1, *x = kuyruk[1] = A.
on == arka (1 == 4)? Hayır.
on = 2, *x = kuyruk[2] = B.
yeni = (4 + 1) % 5 = 0 (sarma!); 0 ≠ on, yer var.
arka = 0, kuyruk[0] = E.
yeni = (0 + 1) % 5 = 1; 1 ≠ on, yer var.
arka = 1, kuyruk[1] = F.
yeni = (1 + 1) % 5 = 2 == on → dolu, false döner. arka 1 olarak korunur. 5 hücrede 4 öğe.
bool kuyruk_ekle(int on, int *arka, element x) { int yeni = (*arka + 1) % MAX_QUEUE_SIZE; if (yeni == on) return false; /* dolu */ *arka = yeni; kuyruk[yeni] = x; return true; } bool kuyruk_al(int *on, int arka, element *x) { if (*on == arka) return false; /* boş */ *on = (*on + 1) % MAX_QUEUE_SIZE; *x = kuyruk[*on]; return true; }
Dairesel Kuyruk
Dolu/Boş Ayrımı: Üç Yöntem
| Yöntem | Boş | Dolu | Kapasite | Bedeli |
|---|---|---|---|---|
| 1. Bir hücre boş bırakmak | on == arka | (arka+1)%MAX == on | MAX − 1 | Bir hücre israf |
| 2. Sayaç tutmak | sayac == 0 | sayac == MAX | MAX | Her işlemde sayaç güncellemesi |
| 3. Bayrak tutmak | on == arka ve son işlem al | on == arka ve son işlem ekle | MAX | Her işlemde bayrak |
Sorunun kökü: yalnız on ve arka ile MAX + 1 farklı doluluk durumu (0, 1, …, MAX öğe) ayırt edilmek istenir, ama iki indisin farkı yalnız MAX farklı değer alabilir. Ya bir durumdan vazgeçilir (yöntem 1) ya da ek bilgi tutulur (yöntem 2, 3).
arka'yı hiç tutmaz: arka = (on + sayac − 1) % KAP.Dairesel Kuyruk · Kodu adım adım çalıştır
Kod İzleme: Sayaçlı Dairesel Kuyruk
KAP = 3, on = 0, sayac = 0. Ekle 1, 2, 3, 4; al; ekle 4; al.
Yer var; konum = (on + sayac) % 3 = 0, veri[0] = 1; sayac = 1.
Yer var; konum = (on + sayac) % 3 = 1, veri[1] = 2; sayac = 2.
Yer var; konum = (on + sayac) % 3 = 2, veri[2] = 3; sayac = 3.
sayac == KAP → dolu. Üç hücrenin üçü de dolu: hiç hücre israf edilmedi.
Boş değil; *x = veri[0] = 1.
on = 1, sayac = 2.
Yer var; konum = (on + sayac) % 3 = 0 (sarma), veri[0] = 4; sayac = 3.
Boş değil; *x = veri[1] = 2.
on = 2, sayac = 2.
bool ekle(Kuyruk *k, int x) { if (k->sayac == KAP) return false; /* dolu */ k->veri[(k->on + k->sayac) % KAP] = x; k->sayac++; return true; } bool al(Kuyruk *k, int *x) { if (k->sayac == 0) return false; /* boş */ *x = k->veri[k->on]; k->on = (k->on + 1) % KAP; k->sayac--; return true; }
Bağlı Kuyruk
Bağlı Liste ile Kuyruk
on ilk düğümü, arka son düğümü gösterir; son düğümün bağı NULL.
- Ekleme arkaya:
arka->bag = yeni; arka = yeni;→ O(1). - Silme önden:
on = on->bag;→ O(1). - Tersi (önden ekle, arkadan sil) olmaz: tek yönlü bağlıda sondan silmek için sondan bir öncekini bulmak O(n).
Bağlı Kuyruk · Kodu adım adım çalıştır
Kod İzleme: Bağlı kuyruk_ekle
Boş kuyruk: on = arka = NULL. Sırayla ekle 10, 20, 30.
malloc: yeni düğüm ayrıldı.
deger = 10, bag = NULL (yeni son düğüm olacak).
*arka == NULL: kuyruk boştu, yeni düğüm aynı zamanda ön olur: *on = yeni.
*arka = yeni: arka 10'u gösterir.
malloc: yeni düğüm ayrıldı.
deger = 20, bag = NULL (yeni son düğüm olacak).
Kuyruk boş değil: (*arka)->bag = yeni, 10 artık 20'u gösterir.
*arka = yeni: arka 20'u gösterir.
malloc: yeni düğüm ayrıldı.
deger = 30, bag = NULL (yeni son düğüm olacak).
Kuyruk boş değil: (*arka)->bag = yeni, 20 artık 30'u gösterir.
*arka = yeni: arka 30'u gösterir.
Kuyruk: 10 → 20 → 30. Her ekleme sabit iş: O(1); arka olmasaydı sonu bulmak O(n) olurdu.
bool kuyruk_ekle(dugum **on, dugum **arka, int x) { dugum *yeni = malloc(sizeof *yeni); if (yeni == NULL) return false; yeni->deger = x; yeni->bag = NULL; if (*arka == NULL) *on = yeni; /* boş kuyruk */ else (*arka)->bag = yeni; /* sona bağla */ *arka = yeni; return true; }
Bağlı Kuyruk · Kodu adım adım çalıştır
Kod İzleme: Bağlı kuyruk_al
Kuyruk: 10 → 20. Üç kez kuyruk_al.
eski = *on.
*x = 10; *on = eski->bag → düğüm(20).
*on ≠ NULL: arka değişmez.
free(eski); main 10 yazar.
eski = *on.
*x = 20; *on = eski->bag → NULL.
*on == NULL: son öğe de çıktı; *arka = NULL yapılmazsa arka serbest bırakılacak düğümü göstermeye devam eder (sarkan işaretçi)!
free(eski); main 20 yazar.
eski == NULL → boş, false döner.
bool kuyruk_al(dugum **on, dugum **arka, int *x) { dugum *eski = *on; if (eski == NULL) return false; /* boş */ *x = eski->deger; *on = eski->bag; if (*on == NULL) *arka = NULL; /* son öğe gitti */ free(eski); return true; }
Bağlı Kuyruk
Kuyruk Gerçeklemelerinin Karşılaştırması
| Ölçüt | Doğrusal dizi | Dairesel dizi | Bağlı liste |
|---|---|---|---|
| ekle / al | O(1), ama “sahte dolu” | O(1) | O(1) (malloc/free) |
| Kaydırma gerekir mi? | Evet: O(n) | Hayır | Hayır |
| Kapasite | MAX | MAX − 1 (ya da sayaçla MAX) | Bellek kadar |
| Ek bellek | Yok | Yok | Öğe başına bir işaretçi |
| Özel durum | on/arka sona dayanır | Sarma ve dolu/boş ayrımı | Boşalınca arka = NULL |
Pratikte en çok dairesel dizi (sabit kapasiteli tampon) ve büyüyen dairesel dizi kullanılır; bağlı kuyruk, öğeler zaten düğüm olarak yaşıyorsa ya da üst sınır bilinmiyorsa tercih edilir.
Deque ve Uygulamalar
Çift Uçlu Kuyruk (Deque)
- Deque (double-ended queue): ekleme ve silme her iki uçta, hepsi O(1).
- Yalnız bir uç kullanılırsa yığın, bir uçtan ekleyip öbüründen alırsa kuyruk olur.
- Kısıtlı türler: girdisi kısıtlı (ekleme tek uçta), çıktısı kısıtlı (silme tek uçta).
- Kullanım: kayan pencere maksimumu, iş çalma (work stealing), geri al + yinele geçmişi.
Deque ve Uygulamalar · Kodu adım adım çalıştır
Kod İzleme: Dairesel Dizide Deque
KAP = 5; sona_ekle ile 10 ve 20 kondu. Şimdi başa 5, başa 3; sonra iki kez sondan al.
sayac = 2 < 5, yer var.
on = (0 − 1 + 5) % 5 = 4: 0'ın bir gerisi dizinin sonu.
veri[4] = 5; sayac = 3.
sayac = 3 < 5, yer var.
on = (4 − 1 + 5) % 5 = 3.
veri[3] = 3; sayac = 4.
Son konum = (3 + 4 − 1) % 5 = 1; *x = 20.
sayac = 3. (on değişmez.)
Son konum = (3 + 3 − 1) % 5 = 0; *x = 10.
sayac = 2. (on değişmez.)
bool basa_ekle(Deque *d, int x) { if (d->sayac == KAP) return false; d->on = (d->on - 1 + KAP) % KAP; /* bir geri */ d->veri[d->on] = x; d->sayac++; return true; } bool sondan_al(Deque *d, int *x) { if (d->sayac == 0) return false; *x = d->veri[(d->on + d->sayac - 1) % KAP]; d->sayac--; return true; }
Deque ve Uygulamalar
Tek Dizide Birden Çok Kuyruk
Çoklu yığında olduğu gibi tek boyutlu bir memory[] dizisi kesimlere bölünür; her kuyruğun on[i], arka[i] ve sinir[i] değerleri tutulur.
Yığındaki “uçlardan büyütme” hilesi kuyrukta işe yaramaz: kuyruk iki ucunda da hareket eder, kesim içinde kayar.
Her kesim kendi başına dairesel kuyruk gibi yönetilirse kaydırma gerekmez; ama bir kesim dolunca yine yer açmak (O(m)) gerekir.
Genellikle her kuyruk için ayrı büyüyen dizi ya da bağlı liste tercih edilir.
Deque ve Uygulamalar · Kodu adım adım çalıştır
Kod İzleme: Round Robin Zamanlama
Üç süreç: P0 = 5, P1 = 2, P2 = 4 birim iş. Zaman dilimi Q = 2 (round robin).
Öndeki P0 alınır, 2 birim çalışır; t = 2.
P0'nin 3 birimi kaldı → kuyruğun sonuna geri döner.
Öndeki P1 alınır, 2 birim çalışır; t = 4.
P1 bitti; kuyruğa dönmez.
Öndeki P2 alınır, 2 birim çalışır; t = 6.
P2'nin 2 birimi kaldı → kuyruğun sonuna geri döner.
Öndeki P0 alınır, 2 birim çalışır; t = 8.
P0'nin 1 birimi kaldı → kuyruğun sonuna geri döner.
Öndeki P2 alınır, 2 birim çalışır; t = 10.
P2 bitti; kuyruğa dönmez.
Öndeki P0 alınır, 1 birim çalışır; t = 11.
P0 bitti; kuyruğa dönmez.
/* k: süreç numaraları kuyruğu; Q: zaman dilimi */ while (kuyruk_al(&k, &p)) { int dilim = kalan[p] < Q ? kalan[p] : Q; zaman += dilim; kalan[p] -= dilim; if (kalan[p] > 0) kuyruk_ekle(&k, p); /* sona geri */ else printf("P%d biter: t=%d\n", p, zaman); }
Deque ve Uygulamalar · Kodu adım adım çalıştır
Kod İzleme: İki Yığınla Kuyruk
Kuyruk iki yığından oluşur. Ekle 1, 2, 3; al; ekle 4; üç kez al.
ekle(1): giriş yığınına push, O(1).
ekle(2): giriş yığınına push, O(1).
ekle(3): giriş yığınına push, O(1).
Çıkış yığını boş → girişteki her şey aktarılacak.
3 giriş'ten pop, çıkış'a push.
2 giriş'ten pop, çıkış'a push.
1 giriş'ten pop, çıkış'a push.
Çıkış yığınının üstü 1: en eski öğe. FIFO sağlandı.
ekle(4): giriş yığınına push, O(1).
Çıkış yığınının üstü 2: en eski öğe. FIFO sağlandı.
Çıkış yığınının üstü 3: en eski öğe. FIFO sağlandı.
Çıkış yığını boş → girişteki her şey aktarılacak.
4 giriş'ten pop, çıkış'a push.
Çıkış yığınının üstü 4: en eski öğe. FIFO sağlandı. Her öğe en çok bir kez aktarıldı → amortize O(1).
void ekle(Kuyruk2 *q, int x) { push(&q->giris, x); } bool al(Kuyruk2 *q, int *x) { int t; if (yigit_bos(&q->cikis)) /* çıkış boşsa */ while (pop(&q->giris, &t)) /* hepsini aktar */ push(&q->cikis, t); return pop(&q->cikis, x); }
Deque ve Uygulamalar
Hangisi Uygun?
| Problem | Yapı |
|---|---|
| Yazıcıya gönderilen belgeler | gösterKuyruk: gönderiliş sırasıyla basılır. |
| Metin düzenleyicide Ctrl+Z | gösterYığın: en son işlem ilk geri alınır. |
| Ağacı seviye seviye yazdırmak | gösterKuyruk (BFS). |
| İç içe parantezlerin denetimi | gösterYığın: kapanan, en son açılanla eşleşir. |
| Fonksiyon çağrıları | gösterYığın (çağrı yığını). |
| Acil serviste en ağır hasta önce | gösterÖncelikli kuyruk: FIFO değil! |
Özet
Karmaşıklık Özeti
| İşlem | Doğrusal dizi | Dairesel dizi | Bağlı | Deque (dairesel) |
|---|---|---|---|---|
| ekle | O(1)* | O(1) | O(1) | O(1) iki uçta |
| al | O(1) | O(1) | O(1) | O(1) iki uçta |
| ön / boş / dolu | O(1) | O(1) | O(1) | O(1) |
| öğe sayısı | arka − on | (arka−on+MAX)%MAX | O(n) ya da sayaç | sayac |
| bellek | MAX | MAX | n · (öğe + işaretçi) | KAP |
* Doğrusal kuyrukta ekleme O(1)’dir ama “sahte dolu” durumunda kaydırma yapılırsa O(n) olur. Round robin benzetimi ve seviye sırası gezinme n öğe için O(n) kuyruk işlemi yapar.
Özet
Sık Yapılan Hatalar
arka == MAX−1'i gerçek doluluk sanmak.% MAX'ı unutup dizi dışına yazmak.arka'yı ilerletip doluysa geri almamak: kuyruk boş görünür.(i − 1) % MAX yazmak: C'de negatif sonuç çıkar.arka = NULL yapmamak.on'u güncellemeyi unutmak.Özet
Alıştırmalar
- MAX = 6, on = 4, arka = 1: kaç öğe var, hangi indislerde?
- Bayrak yöntemiyle dairesel kuyruğun
eklevealyordamlarını yaz. - İki yığınla bir kuyruk yaz;
alişleminin amortize maliyeti nedir? - Kuyruğu yalnız arka işaretçili dairesel bağlı listeyle yaz.
- Round robin örneğini Q = 3 ile çalıştır: süreçler ne zaman biter?
İpuçları / cevaplar
- (1 − 4 + 6) % 6 = 3 öğe: indis 5, 0, 1 (on'un bir sonrasından arka'ya kadar).
- Boş: on == arka && !son_ekleme; dolu: on == arka && son_ekleme. ekle'de bayrak = 1, al'da bayrak = 0.
- Giriş yığınına push; al'da çıkış yığını boşsa girişi tümüyle aktar, sonra pop. Her öğe en çok iki kez taşınır → amortize O(1).
- arka->bag ön düğümdür. Ekleme: yeni->bag = arka->bag; arka->bag = yeni; arka = yeni. Alma: on = arka->bag.
- P0: 0–3, P1: 3–5 (biter t=5), P2: 5–8, P0: 8–10 (biter t=10), P2: 10–11 (biter t=11).
Özet
Bölüm Özeti
Ekleme arkada, alma önde; ilk giren ilk çıkar.
Basit ama arka sona dayanınca öndeki boş yer kullanılamaz; kaydırma pahalı.
% MAX ile sarma; boş on == arka, dolu (arka+1)%MAX == on.
Bir hücre boş bırakılır ya da sayaç veya bayrak tutulur.
on ve arka işaretçisi; son öğe çıkınca arka = NULL.
Deque, iş zamanlama, tampon, iki yığınla kuyruk, BFS.
Özet
Kaynaklar ve Devam
- E. Horowitz, S. Sahni, S. Anderson-Freed, Fundamentals of Data Structures in C, 2. bs., Bölüm 3.3 ve 4.3.
- T. H. Cormen vd., Introduction to Algorithms, 3. bs., Bölüm 10.1 (kuyruklar, deque alıştırmaları).
- R. Sedgewick, Algorithms in C, Kısım 1–4, Bölüm 4.