Bölüm 8: Kuyruk ve Deque
Çoktan Seçmeli · Kuyruk ADT
Soru 1: FIFO Sırası
Boş bir kuyruğa sırasıyla enqueue(4), enqueue(7), dequeue(), enqueue(1), enqueue(9), dequeue() uygulanıyor. Sonunda first() ne döndürür?
FIFO: ilk dequeue 4'ü, ikincisi 7'yi çıkarır; kuyruk (önden arkaya) 1, 9 → first() = 1. 9 diyen yığın (LIFO) gibi düşünmüştür: önde son eklenen değil, en eski öğe durur.
Çoktan Seçmeli · Kuyruk ADT
Soru 2: Hata Fırlatan Çağrı
Boş bir Queue<Integer> q = new LinkedList<>() üzerinde hangi çağrı NoSuchElementException fırlatır?
Hata fırlatan üçlü add/remove/element; offer/poll/peek özel değer döndürür: boşta poll ve peek null, offer başarıda true. peek'in de hata vereceğini sanmak yaygındır; istisna fırlatan karşılığı element()'tir.
Klasik Soru · Kuyruk ADT
Soru 3: Java Kuyruğu Çıktısı
Programın çıktısı nedir?
Queue<Integer> q = new ArrayDeque<>(); for (int i = 1; i <= 4; i++) q.offer(i * 3); q.poll(); q.offer(q.poll() + q.peek()); System.out.println(q.peek() + " " + q.size() + " " + q); while (q.size() > 1) q.poll(); System.out.println(q.poll() + " " + q.poll());
Cevabı göster
- Döngü: [3, 6, 9, 12];
poll()3'ü çıkarır → [6, 9, 12]. - Argüman soldan sağa:
poll()→ 6 (kuyruk [9, 12]),peek()→ 9; 15 arkaya eklenir → [9, 12, 15]. - Önde 9, boyut 3.
while9 ve 12'yi atar; sonrapoll()→ 15, kuyruk boş olduğundan ikincipoll()→null(istisna yok).
Çıktı:
9 3 [9, 12, 15] 15 null
Klasik Soru · Dizi ile Kuyruk
Soru 4: Basit Dizi Kuyruğunun Sorunu
Kapasitesi N = 6 olan basit (dairesel olmayan) dizi kuyruğu on = 0, arka = −1 ile başlıyor. enqueue 10, 20, 30, 40; üç kez dequeue(); enqueue 50, 60 yapılıyor. Son durumda on ve arka nedir ve ardından gelen enqueue(70) neden yapılamaz?
Cevabı göster
- Eklemede
arka++, çıkarmadaon++; öğeler kaydırılmaz. - Dört ekleme →
arka = 3; üç çıkarma (10, 20, 30) →on = 3; 50 ve 60 →arka = 5. - Son durum: on = 3, arka = 5; kuyruk 40, 50, 60 (yalnızca 3 öğe).
arka = N − 1olduğu için kuyruk “dolu” görünür; oysa indis 0–2 boştur ve basit dizi kuyruğu boşalan yuvaları yeniden kullanamaz.
Çözüm 1: her dequeue'da öğeleri sola kaydırmak → dequeue O(n). Çözüm 2: dairesel dizi, indis (i + 1) % N ile başa sarar → her işlem O(1).
Çoktan Seçmeli · Dizi ile Kuyruk
Soru 5: Ekleme İndisi
Dairesel kuyruk bas (ilk öğenin indisi), boyut (öğe sayısı) ve N = dizi.length ile tutuluyor. ekle() yeni öğeyi hangi indise yazar?
Son öğeden sonraki yuva bas + boyut'tur; % N dizinin sonundan başa sarmayı sağlar. (bas + boyut − 1) % N son öğenin kendisidir; mod'suz A ise dizinin dışına taşabilir (ArrayIndexOutOfBoundsException). D, dequeue sonrası yeni bas'tır.
Çoktan Seçmeli · Dizi ile Kuyruk
Soru 6: Bir Yuvayı Boş Bırakma
Dairesel kuyrukta sayaç tutulmuyor; “bir yuvayı boş bırakma” yöntemi kullanılıyor (bas: ilk öğe, arka: sonraki yazma yeri). N = 10 ise kuyruk en fazla kaç öğe saklar ve dolu koşulu nedir?
bas == arka boş koşuludur; dolu ile karışmaması için bir yuva hep boş bırakılır → N − 1 = 9 öğe. Yalnızca bas ve arka ile 10 öğe saklanırsa (A) boş ve dolu kuyrukta ikisi aynı yuvayı gösterir; durumlar ayırt edilemez. Diğer çözüm boyut sayacıdır (dolu: boyut == N).
Adım Adım Çalıştırma · Dizi ile Kuyruk
Soru 7: Dairesel Kuyrukta Başa Sarma
Kapasitesi 4 olan dairesel kuyrukta son satırdaki cikar() ne yazdırır ve sonunda bas ile boyut kaç olur?
Dizi 0'larla dolu ama mantıksal olarak boş.
ekle(10): indis (0 + 0) % 4 = 0.
ekle(20): indis (0 + 1) % 4 = 1.
ekle(30): indis (0 + 2) % 4 = 2.
cikar() → 10; bas 0 → 1. Değer dizide kalır ama artık kuyrukta değildir.
cikar() → 20; bas 1 → 2. Değer dizide kalır ama artık kuyrukta değildir.
ekle(40): indis (2 + 1) % 4 = 3.
ekle(50): indis (2 + 2) % 4 = 0. Dizinin sonu geçildi, başa sarıldı.
ekle(60): indis (2 + 3) % 4 = 1. Dizinin sonu geçildi, başa sarıldı. Kuyruk doldu.
cikar() → 30 yazdırılır (en eski öğe). Son durum: bas = 3, boyut = 3; kuyruk 40, 50, 60.
int[] d = new int[4]; int bas = 0, boyut = 0; void ekle(int x) { d[(bas + boyut) % d.length] = x; boyut++; } int cikar() { int x = d[bas]; bas = (bas + 1) % d.length; boyut--; return x; } // ekle(10); ekle(20); ekle(30); // cikar(); cikar(); // ekle(40); ekle(50); ekle(60); // System.out.println(cikar());
Klasik Soru · Dizi ile Kuyruk
Soru 8: Dolu Kuyruğu Büyütme
Dolu dairesel kuyrukta dizi = [F, G, C, D, E], bas = 2, boyut = 5. ekle(H) çağrısından sonra dizi, bas ve boyut ne olur?
private void buyut(int yeniKap) { E[] yeni = (E[]) new Object[yeniKap]; for (int k = 0; k < boyut; k++) yeni[k] = dizi[(bas + k) % dizi.length]; dizi = yeni; bas = 0; } public void ekle(E oge) { if (boyut == dizi.length) buyut(2 * dizi.length); dizi[(bas + boyut) % dizi.length] = oge; boyut++; }
Cevabı göster
boyut == 5→buyut(10).- k = 0..4: dizi[2] = C, dizi[3] = D, dizi[4] = E, dizi[0] = F, dizi[1] = G → yeni[0..4] = C, D, E, F, G;
bas = 0. - H: (0 + 5) % 10 = 5 → dizi[5] = H;
boyut = 6.
Sonuç: [C, D, E, F, G, H, _, _, _, _], bas = 0, boyut = 6. Kopyalama mantıksal sırayla yapıldığı için sıra korunur. Büyütme O(n) sürer, ama kapasite iki katına çıktığından ekle amortize O(1)'dir.
Çoktan Seçmeli · Dizi ile Kuyruk
Soru 9: Büyütmede Arrays.copyOf
Dolu kuyrukta dizi = [C, D, A, B], bas = 2 (sıra A, B, C, D). Büyütme yandaki satırla yapılıyor ve bas değişmiyor. ekle(E)'den sonra beş kez cikar() sırasıyla ne döndürür?
// buyut() yerine yazılan satır dizi = Arrays.copyOf(dizi, 2 * dizi.length); // bas = 2 olarak kalır
copyOf öğeleri aynı indislere kopyalar: [C, D, A, B, _, _, _, _], bas = 2. E, (2 + 4) % 8 = 6'ya yazılır; cikar dizi[2..6]'yı okur: A, B, null, null, E. Başa sarmış C ve D kaybolur. A şıkkı, öğeleri (bas + k) % N sırasıyla kopyalayan doğru buyut'un sonucudur; D, E'nin 4. indise yazıldığını varsayar.
Klasik Soru · Bağlı Liste ile Kuyruk
Soru 10: enqueue Hatası
Bağlı liste kuyruğunun bu enqueue metodunda bir hata var. Boş kuyrukta enqueue(10), enqueue(15) çağrılınca ne olur ve metot nasıl düzeltilir?
public void enqueue(int veri) { Dugum gecici = new Dugum(veri); if (bosMu()) { on = gecici; } else { arka.sonraki = gecici; } uzunluk++; }
Cevabı göster
enqueue(10): kuyruk boş →on= düğüm(10);arkahiç atanmadığı içinnullkalır.enqueue(15): boş değil →arka.sonrakierişimi →NullPointerException.
Düzeltme: if/else'ten sonra yeni düğüm her iki durumda da arka olmalı:
}
arka = gecici; // eklendi
uzunluk++;Liste gezinilmediği için her enqueue O(1).
Klasik Soru · Kuyruk Türleri
Soru 11: Deque Çıktısı
ArrayDeque çift uçlu kuyruktur; push/pop baş uçta çalışır. Programın çıktısı nedir?
Deque<String> d = new ArrayDeque<>();
d.offerFirst("A");
d.offerLast("B");
d.push("C");
d.addLast("D");
System.out.print(d.pollLast() + " ");
System.out.print(d.pop() + " ");
d.offerFirst("E");
System.out.print(d.peekLast() + " " + d);Cevabı göster
offerFirstA → [A];offerLastB → [A, B]push=addFirst→ [C, A, B];addLastD → [C, A, B, D]pollLast→ D;pop=removeFirst→ C; deque [A, B]offerFirstE → [E, A, B];peekLast→ B (çıkarmaz)
Çıktı: D C B [E, A, B]
Deque FIFO'ya bağlı değildir: yalnızca bir uçtan kullanılırsa yığın, bir uçtan ekleyip diğerinden çıkarılırsa kuyruk olur.
Çoktan Seçmeli · Kuyruk Türleri
Soru 12: onceki Referansı
Çift yönlü bağlı liste ile gerçeklenen deque'de her düğümün onceki referansını da tutmasının asıl nedeni nedir?
Tek yönlü listede sondan çıkarmak için sondan bir önceki düğüm baştan gezilerek bulunur: O(n). basaEkle tek yönlü listede de O(1)'dir (B). onceki her düğüme ek bir referans ekler, yani belleği artırır (D).
Klasik Soru · Kuyruk Türleri
Soru 13: Kayan Pencere Maksimumu
a = {4, 2, 1, 3, 6, 2, 1} için pencereMax(a, 3) ne döndürür?
int[] pencereMax(int[] a, int k) { int[] enb = new int[a.length - k + 1]; Deque<Integer> d = new ArrayDeque<>(); for (int i = 0; i < a.length; i++) { if (!d.isEmpty() && d.peekFirst() <= i - k) d.pollFirst(); // pencere dışı while (!d.isEmpty() && a[d.peekLast()] <= a[i]) d.pollLast(); // aday değil d.offerLast(i); if (i >= k - 1) enb[i - k + 1] = a[d.peekFirst()]; } return enb; }
Cevabı göster
| i | a[i] | d (indis) | enb |
|---|---|---|---|
| 0 | 4 | 0 | |
| 1 | 2 | 0, 1 | |
| 2 | 1 | 0, 1, 2 | 4 |
| 3 | 3 | 3 (0 pencere dışı; 2, 1 atıldı) | 3 |
| 4 | 6 | 4 | 6 |
| 5 | 2 | 4, 5 | 6 |
| 6 | 1 | 4, 5, 6 | 6 |
Sonuç: [4, 3, 6, 6, 6]. Deque'de değerler önden arkaya azalır, en büyük önde durur. Her indis bir kez girer, en çok bir kez çıkar → O(n) (kaba kuvvet O(n·k)).
Klasik Soru · Uygulamalar
Soru 14: Round Robin Çizelgeleyici
Hazır kuyruğunda sırayla P1 (4 birim), P2 (7 birim), P3 (2 birim) var; round robin zaman dilimi (quantum) q = 3. Önden alınan süreç en fazla q birim çalışır, bitmezse arkaya döner. Her süreç hangi anda tamamlanır?
Cevabı göster
| Zaman | Çalışan | Kalan | Kuyruk (sonra) |
|---|---|---|---|
| 0–3 | P1 | 1 | P2, P3, P1 |
| 3–6 | P2 | 4 | P3, P1, P2 |
| 6–8 | P3 | 0 → biter | P1, P2 |
| 8–9 | P1 | 0 → biter | P2 |
| 9–12 | P2 | 1 | P2 |
| 12–13 | P2 | 0 → biter | boş |
Tamamlanma: P3 = 8, P1 = 9, P2 = 13. Kalan süre dilimden kısaysa dilimin tamamı kullanılmaz. Her tur bir poll ve en çok bir offer: O(1).
Adım Adım Çalıştırma · Uygulamalar
Soru 15: Josephus Problemi
Kuyrukta önden arkaya A, B, C, D, E, F varken josephus(q, 2) hangi sırayla eleme yapar ve kimi döndürür?
size 6 > 1 → tur başlar. i = 1: A önden alınıp arkaya geçer.
Öndeki B çıkarılır ve elenir. Kalan: 5 kişi.
size 5 > 1 → tur başlar. i = 1: C önden alınıp arkaya geçer.
Öndeki D çıkarılır ve elenir. Kalan: 4 kişi.
size 4 > 1 → tur başlar. i = 1: E önden alınıp arkaya geçer.
Öndeki F çıkarılır ve elenir. Kalan: 3 kişi.
size 3 > 1 → tur başlar. i = 1: A önden alınıp arkaya geçer.
Öndeki C çıkarılır ve elenir. Kalan: 2 kişi.
size 2 > 1 → tur başlar. i = 1: E önden alınıp arkaya geçer.
Öndeki A çıkarılır ve elenir. Kalan: 1 kişi.
size 1 → döngü biter; return E. Eleme sırası B, D, F, C, A; kazanan E. n − 1 tur, her tur k kuyruk işlemi: O(n·k).
String josephus(Queue<String> q, int k) { while (q.size() > 1) { for (int i = 1; i < k; i++) q.offer(q.poll()); // arkaya String elenen = q.poll(); System.out.println(elenen + " elendi"); } return q.poll(); }
Adım Adım Çalıştırma · Uygulamalar
Soru 16: İki Yığınla Kuyruk
Yorumdaki işlemler sonunda ne yazdırılır ve toplam kaç öğe gelen'den giden'e aktarılır?
enqueue(1): gelen.push(1) → O(1).
enqueue(2): gelen.push(2); üst solda.
dequeue: giden boş → gelen'deki 2 ve 1 aktarılır. Aktarım sırayı ters çevirir; en eski öğe (1) üste çıkar.
giden.pop() → 1 yazdırılır (FIFO).
enqueue(3): gelen.push(3). giden'deki 2'ye dokunulmaz.
dequeue: giden boş değil → aktarım yok; giden.pop() → 2.
dequeue: giden boş → gelen'deki 3 aktarılır.
giden.pop() → 3. Çıktı 1 2 3 = giriş sırası; toplam 3 aktarım. Her öğe en çok bir kez aktarılır → dequeue amortize O(1) (tek çağrı en kötü O(n)).
Deque<Integer> gelen = new ArrayDeque<>(); Deque<Integer> giden = new ArrayDeque<>(); void enqueue(int x) { gelen.push(x); } int dequeue() { if (giden.isEmpty()) while (!gelen.isEmpty()) giden.push(gelen.pop()); return giden.pop(); } // enqueue(1); enqueue(2); // yaz(dequeue()); enqueue(3); // yaz(dequeue()); yaz(dequeue());
Çoktan Seçmeli · Özet
Soru 17: PriorityQueue ve FIFO
Programın çıktısı nedir?
Queue<Integer> q = new PriorityQueue<>();
q.offer(5);
q.offer(1);
q.offer(3);
System.out.println(q.poll() + " " + q.poll());PriorityQueue, Queue arayüzünü uygulasa da FIFO değildir: poll en küçük öğeyi (doğal sıra, min-öncelik) verir → 1, sonra 3. “5 1” diyen, değişkenin türü Queue olduğu için geliş sırası beklemiştir.