Bölüm 11: Arama Ağaçları
Çoktan Seçmeli · İkili Arama Ağacı
Soru 1: Olamayacak Kök Önde Sıra
Aşağıdaki dizilerden hangisi bir ikili arama ağacının kök önde (preorder) gezinme sırası olamaz?
C: 50 > 40 olduğu için 50 ile sağ alt ağaç başlar; bundan sonra gelen her değer 40'tan büyük olmalıdır, oysa 30 gelir. Kök önde sırada kök önce yazılır, ardından önce bütün küçükler (sol alt ağaç), sonra bütün büyükler gelir. D geçerlidir: 20'nin sağında 30, 30'un solunda 25 vardır.
Klasik Soru · Ekleme
Soru 2: Ekleme Sırasından Ağaç
Boş bir ikili arama ağacına arama_agacina_ekle ile sırasıyla 41, 20, 65, 11, 29, 50, 91, 32, 72, 99 ekleniyor. Ağacı çizin. Ağacın derinliği nedir? Kök sonda (postorder) gezinme sırası nedir?
Cevabı göster
Her değer kökten başlayıp küçükse sola, büyükse sağa inerek ilk NULL'a konur: 32, 41 → 20 → 29 yolunu izler ve 29'un sağına; 72, 41 → 65 → 91 yolunu izler ve 91'in soluna girer.
Derinlik 4 (41–20–29–32 ve 41–65–91–72 yolları).
Kök sonda: 11 32 29 20 50 72 99 91 65 41. Kök ortada sıra her zaman sıralıdır: 11 20 29 32 41 50 65 72 91 99.
Çoktan Seçmeli · Ekleme
Soru 3: Farklı Ağaç Kuran Sıra
Boş bir ikili arama ağacına aşağıdaki sıralarla ekleme yapılıyor. Hangisi ötekilerden farklı bir ağaç kurar?
A: 1, 4'ün sol çocuğu olur; sonra gelen 2 ve 3 sağa doğru bir zincir kurar (1 → 2 → 3). B, C ve D'de ise 2, 4'ün sol çocuğudur ve 1 ile 3'ün ebeveynidir. Aynı ağacı veren sıralar çoktur; yeter ki her ata kendi soyundan önce eklensin.
Hata Bulma · Ekleme
Soru 4: Atanmayan Dönüş Değeri
Özyinelemeli ekleme yordamı aşağıdaki gibi yazılmış. Program ne yazdırır, kaç düğüm için bellek ayrılır? Hata nedir, nasıl düzeltilir? (kokortada sol, kök, sağ sırasıyla yazdırır.)
agac_gost ekle(agac_gost L, int x) { if (!L) { L = ogeal(); /* çocuklar NULL */ L->veri = x; } else if (x < L->veri) ekle(L->solcocuk, x); else if (x > L->veri) ekle(L->sagcocuk, x); return L; } /* main içinde */ agac_gost kok = NULL; kok = ekle(kok, 50); ekle(kok, 30); ekle(kok, 70); ekle(kok, 30); kokortada(kok);
Cevabı göster
Çıktı: 50. ogeal 4 kez çağrılır; 50 dışındaki 3 düğüm hiçbir yere bağlanmaz ve sızar.
- Alt çağrı yeni düğümü kendi yerel
L'sine koyup adresini döndürür; çağıran bu değeri atar,L->solcocukNULL kalır. - 30 ağaca hiç girmediği için ikinci
ekle(kok, 30)da yeni bir düğüm ayırır.
else if (x < L->veri) L->solcocuk = ekle(L->solcocuk, x); else if (x > L->veri) L->sagcocuk = ekle(L->sagcocuk, x);
Çağıran taraf da her zaman kok = ekle(kok, x); yazmalıdır; boş ağaçta yeni kök ancak böyle alınır.
Adım Adım Çalıştırma · Arama
Soru 5: Olmayan Değeri Arama
arama_agacinda_ara(70, kok) yukarıdaki (ikinci sorudaki) ağaçta çalıştırılıyor. Hangi düğümlere bakılır, yordam ne döndürür?
x = 70; L kökü (41) gösteriyor.
70 ≠ 41 ve 70 > 41 → sağa: L = 65. Sol alt ağacın tamamı elendi.
70 > 65 → sağa: L = 91. 50 hiç incelenmez.
70 < 91 → sola: L = 72.
70 < 72 → sola: L = 72->solcocuk = NULL.
Döngü koşulu yanlış → NULL. 4 düğüme bakıldı (derinlik kadar). 70 eklenseydi 72'nin sol çocuğu olurdu.
agac_gost arama_agacinda_ara(int x, agac_gost L) { while (L) { if (x == L->veri) return L; if (x < L->veri) L = L->solcocuk; else L = L->sagcocuk; } return NULL; }
Çoktan Seçmeli · Arama
Soru 6: Unutulan return
Özyinelemeli arama aşağıdaki gibi yazılmıştır. Değerleri farklı, en az iki düğümlü bir ağaçta ara(x, kok) için hangisi doğrudur?
agac_gost ara(int x, agac_gost L) { if (!L) return NULL; if (x == L->veri) return L; if (x < L->veri) ara(x, L->solcocuk); else ara(x, L->sagcocuk); return NULL; }
D: Alt çağrı düğümü bulsa da döndürdüğü adres kullanılmadan atılır; akış en sondaki return NULL'a düşer. Adres yalnızca x == L->veri ilk çağrıda, yani kökte sağlanırsa döner. Kod geçerli C'dir ve derlenir; özyineleme NULL'a ulaşınca biter.
Adım Adım Çalıştırma · Derinlik
Soru 7: derinlik() Çağrı Yığını
derinlik(kok) çağrılıyor. Çağrılar (yığın: alttan üste) hangi sırayla açılıp kapanır, sonuç nedir? (Düğümün yanındaki sayı, döndürdüğü değerdir.)
Kökte sol ve sağ henüz bilinmiyor; önce sol alt ağacın derinliği istenir.
Her çağrının kendi sol/sag yerelleri vardır; 50'ninkiler yığında bekler.
20 yaprak: iki NULL çağrısı 0 döndürür → MAX(0, 0) + 1 = 1.
30'a dönüldü: sol = 1. Şimdi sağ alt ağaç: derinlik(40).
Yığın en derin hâlinde: 4 çerçeve (+ NULL çağrıları). 35 → 1.
40: sağı NULL (0) → MAX(1, 0) + 1 = 2.
30: MAX(1, 2) + 1 = 3; çerçeve silinir.
50'de sol = 3 saklandı; 60 yaprak → 1.
MAX(3, 1) + 1 = 4. Toplam 13 çağrı: 6 düğüm + 7 NULL. Zaman O(n), yığın O(h).
int derinlik(agac_gost L) { int sol, sag; if (L) { sol = derinlik(L->solcocuk); sag = derinlik(L->sagcocuk); return MAX(sol, sag) + 1; } else return 0; }
Çoktan Seçmeli · Derinlik
Soru 8: MAX Makrosunda Çağrı Sayısı
Derinlik tek satırda, makroyla yazılmıştır. Yalnızca kök ve onun iki yaprak çocuğundan oluşan ağaçta derinlik(kok) yordamı (ilk çağrı dahil) toplam kaç kez çalışır?
#define MAX(a,b) ((a) > (b) ? (a) : (b)) int derinlik(agac_gost L) { return !L ? 0 : MAX(derinlik(L->solcocuk), derinlik(L->sagcocuk)) + 1; }
B: Yaprakta MAX(a, b) önce iki çağrıyı yapar; 0 > 0 yanlış olduğu için (b) ifadesi bir kez daha değerlendirilir. Yaprak başına 1 + 3 = 4 çağrı. Kökte de büyük olan (eşitlikte b) yeniden hesaplanır: 1 + 4 + 4 + 4 = 13. Yerel değişkenli sürüm 7 çağrıyla (3 düğüm + 4 NULL) biter.
Kod Yazma · Arama
Soru 9: Taban Değeri Bulmak
İkili arama ağacında x'ten büyük olmayan en büyük değeri içeren düğümü (taban) döndüren agac_gost taban(int x, agac_gost L) yordamını döngüsel ve O(h) olarak yazın; böyle bir değer yoksa NULL dönsün. İkinci sorudaki ağaçta taban(30) 29'u, taban(64) 50'yi, taban(10) NULL'u vermelidir.
Cevabı göster
agac_gost taban(int x, agac_gost L) { agac_gost aday = NULL; /* en iyi aday */ while (L) { if (x == L->veri) return L; /* tam eşleşme */ if (x < L->veri) L = L->solcocuk; else { /* L->veri < x */ aday = L; L = L->sagcocuk; } } return aday; }
Sağa dönülen her düğüm x'ten küçüktür ve aday olur; daha büyük bir aday ancak onun sağ alt ağacında bulunabilir. Sola dönülen düğüm x'ten büyüktür, aday olamaz.
Tek bir kök–yaprak yolu izlenir: O(h), ek bellek O(1).
Klasik Soru · Silme
Soru 10: İki Çocuklu Düğüm Silme
İkinci sorudaki ağaca sunumdaki sil yordamı (sol alt ağacın en büyüğünün verisi taşınır) uygulanıyor: önce sil(41, &kok), sonra sil(20, &kok). Her silmeden sonra ağaç nasıl görünür? Hangi düğümler free edilir?
Cevabı göster
sil(41): iki çocuk var. Sol alt ağaçta sağa inilir: 20 → 29 → 32; 32'nin sağı yok, en büyük odur. Kökün veri alanına 32 yazılır; 32'nin düğümü free edilir, 29'un sağ bağı onun sol çocuğunu (NULL) alır.
sil(20): sol alt ağacın en büyüğü 11'dir (sağı yok, döngü hiç dönmez). 20'nin düğümüne 11 yazılır, eski 11 düğümü free edilir; b = &p->solcocuk olduğundan bu düğümün sol bağı NULL olur.
İki silmede de yalnızca bir bağ değişti; derinlik 4 kaldı.
Çoktan Seçmeli · Silme
Soru 11: Son Satır Değişirse
sil yordamında *bag = p->solcocuk ? p->solcocuk : p->sagcocuk; satırı *bag = NULL; yapılırsa hangisi doğrudur?
A: Yaprakta iki çocuk da NULL'dır, sonuç değişmez. Tek çocuklu düğümde çocuk ve bütün alt ağacı ağaçtan kopar ve sızar. İki çocuklu silmede de taşınan öncülün sol çocuğu varsa (sunum örneğinde 57'nin 56'sı) o alt ağaç kaybolur.
Adım Adım Çalıştırma · Dengeli Ağaçlar
Soru 12: AVL'ye Ekleme ve Döndürme
40, 20, 60, 10, 30 eklenmiş AVL ağacına sırasıyla 25, 27, 5 ve 4 ekleniyor. Hangi eklemede hangi döndürme yapılır, son ağaç nedir? (Kırmızı sayılar denge faktörüdür.)
Başlangıç: bütün faktörler −1..+1 aralığında.
25, 30'un soluna girer. Dönüşte 40'ın faktörü +2; x = 25 > 20 (sol çocuk) → LL değil, LR.
Önce sol çocuk 20 sola döndürülür: 30 yukarı çıkar, 25 20'nin sağına geçer (LL biçimi).
Sonra 40 sağa döndürülür: torun 30 iki atasının üstüne çıkar. Yükseklik eklemeden önceki gibi 3.
27: 25'in sağına. Faktörler −1, −1, +1: döndürme yok.
5: 10'un soluna. 20'nin iki alt ağacı da 2 yüksekliğinde (faktör 0); döndürme yok.
4: 5'in soluna. 10'un faktörü +2 ve x = 4 < 5 (sol çocuk) → LL.
10 sağa döndürülür, 5 alt ağacın kökü olur. Son yükseklik 4; aynı sıra sıradan BST'de 5 düzeylik ağaç kurardı.
avl_gost avl_ekle(avl_gost t, int x) { if (!t) return yeni_avl(x); if (x < t->veri) t->sol = avl_ekle(t->sol, x); else if (x > t->veri) t->sag = avl_ekle(t->sag, x); else return t; t->yuk = 1 + MAX(yuk(t->sol), yuk(t->sag)); int d = denge(t); if (d > 1 && x < t->sol->veri) /* LL */ return sag_dondur(t); if (d < -1 && x > t->sag->veri) /* RR */ return sol_dondur(t); if (d > 1) { /* LR */ t->sol = sol_dondur(t->sol); return sag_dondur(t); } if (d < -1) { /* RL */ t->sag = sag_dondur(t->sag); return sol_dondur(t); } return t; }
Çoktan Seçmeli · Dengeli Ağaçlar
Soru 13: Dengeli Ağaçlar Hakkında
Dengeli arama ağaçları hakkındaki ifadelerden hangisi yanlıştır?
D yanlıştır: silme bir alt ağacın yüksekliğini azaltır ve bu azalma köke kadar sürebilir; yol boyunca O(lg n) döndürme gerekebilir. Eklemede ise döndürme, alt ağacın yüksekliğini eklemeden önceki değerine getirdiği için yukarıdaki atalar etkilenmez.
Çoktan Seçmeli · Dengeli Ağaçlar
Soru 14: Döndürmede Atama Sırası
sag_dondur yordamında ilk iki bağ atamasının sırası aşağıdaki gibi değiştirilirse ne olur?
avl_gost sag_dondur(avl_gost k2) {
avl_gost k1 = k2->sol;
k1->sag = k2; /* sıra değişti */
k2->sol = k1->sag;
k2->yuk = 1 + MAX(yuk(k2->sol), yuk(k2->sag));
k1->yuk = 1 + MAX(yuk(k1->sol), k2->yuk);
return k1;
}B: Önce k1->sag = k2 yapılınca k1'in eski sağ alt ağacının (T2) adresi silinir. Ardından k2->sol = k1->sag, yani k2->sol = k2 olur: kendine dönen bir bağ. Gezinme bu düğümde sonsuza girer, T2 sızar. Doğru sırada önce T2 k2'nin soluna taşınır, sonra k2 k1'in sağına bağlanır.
Çoktan Seçmeli · Sınav Soruları
Soru 15: SWAP Arada: Düzeltme
Sınav sorusundaki yordamda SWAP iki özyinelemeli çağrının arasındadır ve ağaç tam aynasına dönmez. İkinci çağrı (işaretli satır) hangisiyle değiştirilirse yordam ağacın tam aynasını üretir?
#define SWAP(x,y,t) ((t)=(x),(x)=(y),(y)=(t)) void yordam(agac_gost L) { agac_gost p; if (L) { yordam(L->solcocuk); SWAP(L->solcocuk, L->sagcocuk, p); yordam(L->sagcocuk); /* ikinci çağrı */ } }
C: SWAP'tan sonra işlenmemiş olan eski sağ alt ağaç solda durur; yordam(L->solcocuk) onu işler. {44, 30, 15, 18, 86, 97, 65, 113, 74, 129} ağacında kök ortada çıktı 129 113 97 86 74 65 44 30 18 15 olur. A eski sol alt ağacı ikinci kez işleyip geri çevirir; D eski sağ alt ağacı hiç çevirmez; B sonsuz özyinelemedir.
Klasik Soru · Sınav Soruları
Soru 16: AgirlikBul Çağrı Sayısı
Sınav sorusundaki hatalı AgirlikBul yordamı, kökü 5 ve yalnızca sol çocuğu 3 olan ağaçta çağrılıyor. i) x = 3 ve ii) x = 9 için yordam (ilk çağrı dahil) kaç kez çalışır?
int AgirlikBul(oge_gost L, int x) { if (!L) return 0; else if (L->veri == x) return agirlik(L); if (AgirlikBul(L->solcocuk, x) > AgirlikBul(L->sagcocuk, x)) AgirlikBul(L->solcocuk, x); if (AgirlikBul(L->sagcocuk, x) > AgirlikBul(L->solcocuk, x)) AgirlikBul(L->sagcocuk, x); return 0; }
Cevabı göster
NULL ile yapılan çağrı 1 sayılır. x bulunmazsa her düğüm, dört karşılaştırma terimi için alt ağaçlarını dört kez çağırır; > hiç doğru olmadığı için içerideki çağrılar yapılmaz.
- x = 9: yaprak 3 için 1 + 4 = 5 çağrı. Kök: 1 + A(3) + A(NULL) + A(NULL) + A(3) = 1 + 5 + 1 + 1 + 5 = 13.
- x = 3: A(3) hemen
agirlikile 1 döndürür (1 çağrı). Kök: ilk if'te A(3) = 1 > A(NULL) = 0 doğrudur, A(3) bir kez daha çağrılır; ikinci if'te A(NULL) ve A(3) değerlendirilir: 1 + 1 + 1 + 1 + 1 + 1 = 6.
Her ara çağrı sonucu bir kez hesaplanıp yerel değişkende tutulsaydı bu tekrarlar olmazdı.