Bölüm 3: Özyineleme
Çoktan Seçmeli · Özyineleme Kavramı
Soru 1: En Derin An
main f(4)'ü çağırıp sonucu yazdırıyor. Çağrı yığını en derin anında main dahil kaç çerçeve içerir?
static int f(int n) { if (n <= 1) return 1; return n * f(n - 1); } // main içinde: System.out.println(f(4));
B: main, f(4), f(3), f(2) ve f(1) aynı anda yığındadır: her çağrının kendi çerçevesi ve kendi n'si vardır. f(1) temel durumdur, yeni çağrı açmaz. Çerçeveler ters sırayla kapanırken çarpmalar yapılır: 1, 2, 6, 24.
Çıktı Sorusu · Faktöriyel ve Toplam
Soru 2: İnişte ve Dönüşte Yazdırma
Metot hem kendini çağırmadan önce hem de çağrı döndükten sonra yazdırıyor. Çıktı nedir?
static int f(int n) { if (n == 0) return 0; System.out.print(n + " "); int s = n + f(n - 1); System.out.print("[" + s + "] "); return s; } // main içinde: System.out.println(f(3));
Cevabı göster
Çıktı: 3 2 1 [1] [3] [6] 6
- İniş: her çağrı önce kendi n'sini yazar, sonra bir küçüğünü çağırır: 3 2 1. f(0) temel durumdur, hiçbir şey yazmaz.
- Dönüş: çerçeveler ters sırayla kapanır. f(1): s = 1 + 0; f(2): s = 2 + 1 = 3; f(3): s = 3 + 3 = 6.
- En son main dönen 6'yı yazar. Metot 1'den n'ye kadar toplamı hesaplar.
Çoktan Seçmeli · Faktöriyel ve Toplam
Soru 3: toplam(0)
toplam(0) çağrılınca ne olur?
static int toplam(int n) { if (n == 1) return 1; return n + toplam(n - 1); }
A: n: 0, −1, −2, … hiçbir zaman 1 olmaz; her çağrı yeni bir çerçeve açar ve yığın dolar. Derleyici bunu göremez, çünkü her yol bir return ile bitiyor. Güvenli temel durum: if (n <= 0) return 0;
Çoktan Seçmeli · Seriler
Soru 4: Fibonacci Çağrı Sayısı
fibonacci(6) çağrısı, ilk çağrı dahil toplam kaç kez fibonacci çağırır?
static int fibonacci(int n) { if (n <= 1) return n; return fibonacci(n - 1) + fibonacci(n - 2); }
D: Çağrı sayısı Ç(n) = 1 + Ç(n − 1) + Ç(n − 2), Ç(0) = Ç(1) = 1: 1, 1, 3, 5, 9, 15, 25. Sonuç ise yalnızca F(6) = 8'dir. Aynı değerler defalarca hesaplandığı için çağrı sayısı üstel büyür.
Çıktı Sorusu · Seriler
Soru 5: Lucas ve Catalan
Program ne yazdırır?
static int lucas(int n) { if (n == 0) return 2; if (n == 1) return 1; return lucas(n - 1) + lucas(n - 2); } static long catalan(int n) { if (n <= 1) return 1; long sonuc = 0; for (int i = 0; i < n; i++) sonuc += catalan(i) * catalan(n - i - 1); return sonuc; } // main içinde: for (int n = 0; n <= 6; n++) System.out.print(lucas(n) + " "); System.out.println(); System.out.println(catalan(4));
Cevabı göster
2 1 3 4 7 11 18 14
- Lucas, Fibonacci ile aynı formülü kullanır; yalnızca başlangıç değerleri (2 ve 1) farklıdır.
- C(4) = C(0)C(3) + C(1)C(2) + C(2)C(1) + C(3)C(0) = 5 + 2 + 2 + 5 = 14. Özyineleme bir döngünün içinde yapılıyor.
Adım Adım Çalıştırma · Seriler
Soru 6: lucas(3) Çağrı Ağacı
Ağaç özyinelemesi: metot kendini iki kez çağırıyor. Çağrıların sırasını ve dönen değerleri izleyin.
n = 3 temel durum değil. Toplamın önce sol terimi: lucas(2).
n = 2: yine sol terimden başlar, lucas(1) çağrılır.
Temel durum: 1 döner.
Sağ terim lucas(0): temel durum, 2 döner.
lucas(2) = 1 + 2 = 3 döner.
lucas(3)'ün sağ terimi lucas(1): 1. Bu değer daha önce hesaplanmıştı; yeniden hesaplanır.
lucas(3) = 3 + 1 = 4 döner.
Toplam 5 çağrı. Yığın en çok 4 çerçeve derinliğe ulaştı.
static int lucas(int n) { if (n == 0) return 2; if (n == 1) return 1; return lucas(n - 1) + lucas(n - 2); } // main içinde: System.out.println(lucas(3));
Çoktan Seçmeli · Metot Yazma
Soru 7: Hangisi Sonlanmaz?
Gövdeler static int f(int n) metoduna aittir. Hangisinde f(7) çağrısı sonlanmaz?
C: n: 7, 4, 1, −2, −5, … 0'ı atlar; temel duruma hiç ulaşılmaz → StackOverflowError. İkincide 7 zaten temel durumdur. Üçüncüde 7 → 8 → 4 → 2 → 1. Dördüncüde her çağrı n'yi küçültür ve n ≤ 1'de durur.
Hata Bulma · Metot Yazma
Soru 8: Eksik return
Metot bir sayının en büyük rakamını bulmak için yazıldı. Derlenir mi? Sorunları bulun ve düzeltin. Düzeltilmiş metot 48729 ve 3152 için ne döndürür?
static int enBuyuk(int n) { if (n < 10) { return n; } else if (n % 10 > enBuyuk(n / 10)) { return n % 10; } else if (n % 10 <= enBuyuk(n / 10)) { return enBuyuk(n / 10); } }
Cevabı göster
- Derlenmez:
error: missing return statement. Sonelse if'in koşulu, öncekinin tersi olsa da derleyici koşulları değerlendirmez; hiçbir dal çalışmazsa metot değersiz biter. - Ayrıca aynı
enBuyuk(n / 10)iki kez çağrılıyor: her basamakta çağrı sayısı ikiye katlanır.
static int enBuyuk(int n) { if (n < 10) return n; int geri = enBuyuk(n / 10); return Math.max(n % 10, geri); }
Sonuçlar: 48729 → 9, 3152 → 5. Alt problemin sonucu bir kez hesaplanıp değişkende tutulur.
Çoktan Seçmeli · Örnekler
Soru 9: Palindrom Kaç Çağrı?
palindromMu("abcdba") çağrısından sonra sayac kaçtır, metot ne döndürür?
static int sayac = 0; static boolean palindromMu(String s) { sayac++; int n = s.length(); if (n <= 1) return true; if (s.charAt(0) != s.charAt(n - 1)) return false; return palindromMu(s.substring(1, n - 1)); }
D: "abcdba": uçlar a = a → "bcdb"; uçlar b = b → "cd"; c ≠ d → false. Üçüncü çağrı ikinci temel durumda (uçlar farklı) biter ve false değişmeden yukarı taşınır. Her çağrı dizgiyi iki karakter kısaltır.
Çıktı Sorusu · Örnekler
Soru 10: Öklid Zinciri
Her çağrı kendi argümanlarını yazdırıyor. Program ne yazdırır?
static int gcd(int x, int y) { System.out.print("(" + x + "," + y + ") "); if (y == 0) return x; return gcd(y, x % y); } // main içinde: System.out.println(gcd(1071, 462)); System.out.println(gcd(17, 5)); System.out.println(gcd(6, 24));
Cevabı göster
(1071,462) (462,147) (147,21) (21,0) 21 (17,5) (5,2) (2,1) (1,0) 1 (6,24) (24,6) (6,0) 6
- 1071 % 462 = 147, 462 % 147 = 21, 147 % 21 = 0 → EBOB 21.
- 17 ile 5 aralarında asaldır: EBOB 1.
- x < y ise ilk adım yalnızca yer değiştirir: 6 % 24 = 6 → gcd(24, 6).
- Son iş her zaman çağrının kendisi olduğundan gcd kuyruk özyinelemelidir; değer çerçevelerden değişmeden geri taşınır.
Adım Adım Çalıştırma · Örnekler
Soru 11: ikili(13) İzleme
Metot, sayının ikilik gösterimini yazdırıyor. Yazdırma çağrıdan sonra yapılıyor. Çağrı yığınını ve çıktıyı izleyin.
13 > 1: önce ikili(6). Yazdırma, çağrı dönene kadar bekler.
6 > 1 → ikili(3).
3 > 1 → ikili(1).
1 > 1 yanlış: temel durum. 1 % 2 = 1 yazılır.
Dönüşte bekleyen yazdırma: 3 % 2 = 1.
6 % 2 = 0.
13 % 2 = 1.
Kalanlar ters sırayla yazıldı: en yüksek basamak, en derindeki çağrıdan geldi. 13 = 8 + 4 + 1 = 1101₂.
static void ikili(int n) { if (n > 1) { ikili(n / 2); } System.out.print(n % 2); } // main içinde: ikili(13);
Çıktı Sorusu · Örnekler
Soru 12: Hanoi: Hedef B
Temel durum n = 0 olan sürüm (kay: kaynak, hed: hedef, yrd: yardımcı). Diskler A'dan B'ye, C yardımcı kullanılarak taşınıyor: coz(3, 'A', 'B', 'C'); Hangi hamleler yazılır? 5 disk için kaç satır yazılırdı?
static void coz(int n, char kay, char hed, char yrd) { if (n == 0) return; coz(n - 1, kay, yrd, hed); System.out.println(n + ": " + kay + "->" + hed); coz(n - 1, yrd, hed, kay); }
Cevabı göster
1: A->B 2: A->C 1: B->C 3: A->B 1: C->A 2: C->B 1: A->B
- Üstteki 2 disk önce yardımcıya (C) taşınır (3 hamle), en büyük disk A → B, sonra 2 disk C → B (3 hamle).
- Hamle sayısı H(n) = 2 · H(n − 1) + 1 = 2n − 1: 3 disk için 7, 5 disk için 31.
Çoktan Seçmeli · Verimlilik
Soru 13: Çok Derin Özyineleme
Sonucun taşması bir yana, varsayılan JVM ayarlarıyla faktoriyel(100000) çağrısında büyük olasılıkla ne olur?
static long faktoriyel(int n) { if (n <= 1) return 1; return n * faktoriyel(n - 1); }
B: Her çağrı yığında bir çerçeve tutar ve yığının boyutu sınırlıdır; derinlik on binleri aşınca JVM StackOverflowError fırlatır. Derleyici çalışma zamanındaki derinliği bilemez. Döngülü sürüm tek çerçeve ve sabit bellekle çalışır.
Çoktan Seçmeli · Kuyruk Özyinelemesi
Soru 14: Hangisi Kuyruk Özyinelemeli?
Aşağıdaki return satırlarından hangisi kuyruk özyinelemeli bir çağrıdır?
A: Kuyruk çağrısında çağrı metodun son işidir; toplama çağrıdan önce yapılır ve sonuç biriktiricide taşınır. Diğerlerinde çağrı döndükten sonra yapılacak bir iş (çarpma, toplama, birleştirme) kalır. Java kuyruk çağrılarını optimize etmez; derinlik yine yığında birikir.
Kod Yazma · Kuyruk Özyinelemesi
Soru 15: Kuyruk Özyinelemeli Üs
Tabanın üssünü kuyruk özyinelemesiyle hesaplayan long usK(int taban, int us, long birikim) metodunu ve kullanıcının biriktiriciyi düşünmesini gerektirmeyen sarmalayıcı bir us metodunu yazın. us(3, 4) ve us(2, 40) kaçtır?
Cevabı göster
static long usK(int taban, int us, long birikim) { if (us == 0) return birikim; return usK(taban, us - 1, birikim * taban); } static long us(int taban, int us) { return usK(taban, us, 1); }
- Çarpma çağrıdan önce yapılır: usK(3, 4, 1) → usK(3, 3, 3) → usK(3, 2, 9) → usK(3, 1, 27) → usK(3, 0, 81) → 81.
- Biriktiricinin başlangıcı çarpmanın etkisiz elemanı 1'dir. long sayesinde us(2, 40) = 1099511627776 taşmaz.
Adım Adım Çalıştırma · Hafızalama
Soru 16: Hafızalı fib(5) İzleme
Hesaplanan değerler bir Map'te saklanıyor. Hangi çağrılar hesap yapar, hangileri hafızadan döner?
fib(5): hafızada yok, temel durum değil → sol terim fib(4).
fib(4) → fib(3) → fib(2): hiçbiri hafızada değil, sol kol iner.
fib(1): temel durum, 1 döner (hafızaya yazılmaz).
fib(0): temel durum, 0 döner.
fib(2) = 1 + 0 = 1, hafızaya yazılır.
fib(3)'ün sağ terimi fib(1): 1.
fib(3) = 1 + 1 = 2, saklanır.
fib(4)'ün sağ terimi fib(2) hafızada: hesaplamadan 1 döner.
fib(4) = 2 + 1 = 3, saklanır.
fib(5)'in sağ terimi fib(3) hafızadan: 2.
fib(5) = 3 + 2 = 5.
Toplam 9 çağrı (2 · 5 − 1); hafızasız sürüm 15 çağrı yapardı.
static Map<Integer, Long> hafiza = new HashMap<>(); static long fib(int n) { if (hafiza.containsKey(n)) return hafiza.get(n); if (n <= 1) return n; long sonuc = fib(n - 1) + fib(n - 2); hafiza.put(n, sonuc); return sonuc; } // main içinde: System.out.println(fib(5));
Çoktan Seçmeli · Hafızalama
Soru 17: Hafızalı Çağrı Sayısı
Önceki sorudaki hafızalı fib metodu, hafıza boşken fib(10) için kaç çağrı yapar?
C: Her n (2…10) bir kez hesaplanır; sol kol 10'dan 0'a 11 çağrıyla iner, n = 2…9 için sağ terimler 8 çağrı daha yapar: 2 · 10 − 1 = 19. 177 hafızasız sürümün çağrı sayısı, 55 ise F(10) değeridir.