Bölüm 9: Ağaçlar
Giriş
Öğrenme Çıktıları
Kök, ebeveyn, çocuk, yaprak, derinlik ve yükseklik terimlerini açıklamak.
Ağaçları düğüm bağlantıları ve dizilerle temsil etmek.
Kök başta, kök ortada, kök sonda ve seviye sırası gezinmeleri uygulamak; özyinelemeyi çağrı yığınıyla izlemek.
Genel ve ikili ağaçları kullanım alanları ve işlem maliyetleriyle karşılaştırmak.
Ağaç Kavramı
Ağaç (Tree)
- Doğrusal olmayan, hiyerarşik bir veri yapısıdır.
- Düğümler (node) ve onları bağlayan kenarlardan (edge) oluşur.
- En üstte tek bir kök (root) bulunur; ağaç kökten aşağı doğru ters çizilir.
- Kök dışındaki her düğümün tam olarak bir ebeveyni vardır; bu yüzden döngü yoktur.
- Her düğümün sıfır veya daha fazla çocuğu olabilir.
Ağaç Kavramı
Günlük Hayatta Ağaçlar: Hiyerarşi
Ağaç Kavramı
Ağaç mı?
Bu yapı bir ağaç mıdır? 16 düğüm var.
Evet. Bağlantılı, döngüsüz ve 16 düğüme 15 kenar. Herhangi bir düğüm (ör. I) kök seçilip ağaç olarak çizilebilir.
Ağaç olmayanlar: döngü içeren, bir düğümü iki ebeveynli olan veya parçaları bağlantısız yapılar.
Ağaç Kavramı
Temel Unsurlar: Kök, İç Düğüm, Yaprak
Kök de (çocuğu varsa) bir iç düğümdür. Tek düğümlü ağaçta kök aynı zamanda yapraktır.
Ağaç Kavramı
Ağaç İlişkileri
| Terim | Anlamı (sağdaki ağaçta) |
|---|---|
| Ebeveyn (parent) | Hemen üstteki düğüm: D, H'nin ebeveyni |
| Çocuk (child) | Hemen alttaki düğüm: H, I, J → D'nin çocukları |
| Kardeş (sibling) | Ebeveyni aynı olanlar: H, I, J |
| Ata (ancestor) | Köke giden yoldaki düğümler: M'nin ataları H, D, A |
| Soy (descendant) | Alt ağaçtaki düğümler: D'nin soyu H, I, J, M |
Ağaç Kavramı
Terimler Ağaç Üzerinde
Kök: ebeveyni olmayan tek düğüm → A.
Ebeveyn / çocuk: D, H, I ve J'nin ebeveynidir; H, I, J de D'nin çocuklarıdır.
Kardeşler: H, I, J (ebeveynleri D). F ile G kardeş değildir.
Yapraklar: K, L, F, G, M, I, J (çocuksuz düğümler).
İç düğümler: A, B, C, D, E, H (en az bir çocuğu olanlar).
Atalar: M'nin ataları H, D, A. Tersine M; H, D ve A'nın soyundandır.
Derece (degree) = çocuk sayısı: A 3, B 2, C 1, D 3 … Ağacın derecesi = en büyük derece = 3.
Derinlik (depth): kökten düğüme kenar sayısı. E'nin derinliği 2 (A→B→E).
Yükseklik (height): kökten en uzak yaprağa kenar sayısı = 3 (A→B→E→K).
Alt ağaç (subtree): D ve tüm soyu (H, I, J, M) kendi başına bir ağaçtır.
Ağaç Kavramı
Derece, Derinlik, Yükseklik
| Ölçü | Tanım | Önceki ağaçta |
|---|---|---|
| Derece (degree) | Bir düğümün çocuk sayısı (alt ağaç sayısı) | D → 3 |
| Derinlik (depth) | Kökten o düğüme kadar olan yoldaki kenar sayısı; kökün derinliği 0 | E → 2 |
| Seviye (level) | Aynı derinlikteki düğümlerin kümesi | seviye 2: E … J |
| Düğüm yüksekliği | Düğümden altındaki en uzak yaprağa kenar sayısı; yaprağın yüksekliği 0 | D → 2 |
| Ağacın yüksekliği | Kökün yüksekliği = en büyük derinlik | 3 |
Ağaç Kavramı
Derinlik ve Yükseklik
| Düğüm | Yükseklik | Derinlik |
|---|---|---|
| A | 3 | 0 |
| B | 1 | 1 |
| C | 0 | 1 |
| D | 1 | 1 |
| E | 2 | 1 |
| F | 0 | 2 |
| G | 0 | 2 |
| H | 0 | 2 |
| I | 0 | 2 |
| J | 1 | 2 |
| K | 0 | 3 |
Ağaç Kavramı
Yol ve Alt Ağaç
- Yol (path): ardışık düğümleri kenarlarla bağlayan dizi.
p = (v₀, e₁, v₁, …, ek, vk) start(p) = v₀,end(p) = vk,length(p) = k(kenar sayısı).- Ağaçta iki düğüm arasında tam olarak bir basit yol vardır; ikinci bir yol olsaydı döngü oluşurdu.
- Alt ağaç (subtree): bir düğüm ve onun tüm soyu; kendisi de bir ağaçtır. Ör. bir klasör ve altındaki her şey.
Ağaç Kavramı
Temel Ağaç Türleri
| Tür | Özellik |
|---|---|
| Genel ağaç | Bir düğümün çocuk sayısına sınır yoktur. |
| k-lı ağaç (k-ary) | Her düğümün en fazla k çocuğu vardır. |
| İkili ağaç (binary) | Her düğümün en fazla iki çocuğu vardır: sol ve sağ. |
| İkili arama ağacı (BST) | İkili ağaç + her düğümde: sol alt ağaçtaki anahtarlar küçük, sağdakiler büyük. |
Ağaç Kavramı
Ağaç Soyut Veri Tipi (Tree ADT)
public interface Agac<E> extends Iterable<E> { Konum<E> kok(); Konum<E> ebeveyn(Konum<E> p); Iterable<Konum<E>> cocuklar(Konum<E> p); int cocukSayisi(Konum<E> p); boolean icMi(Konum<E> p); // isInternal boolean yaprakMi(Konum<E> p); // isExternal boolean kokMu(Konum<E> p); // isRoot int size(); boolean isEmpty(); Iterable<Konum<E>> konumlar(); // positions } // Konum<E> (Position): E getElement();
İkili ağaç ADT'si bunlara sol(p), sag(p), kardes(p) ekler (Goodrich: left, right, sibling).
Konum (position), düğümün kendisini gizleyen bir "yer" soyutlamasıdır: kullanıcı yalnızca öğeye ve ADT metotlarına erişir.
Ağaç Kavramı · Kodu adım adım çalıştır
Kod İzleme: ADT ile derinlik(G) ve yukseklik(B)
derinlik(G): G kök değil → ebeveyni E için derinlik(E) çağrılır (yukarı çıkılır).
derinlik(E): E kök değil → ebeveyni B için derinlik(B) çağrılır (yukarı çıkılır).
derinlik(B): B kök değil → ebeveyni A için derinlik(A) çağrılır (yukarı çıkılır).
derinlik(A): A kök → return 0. Taban durum; artık çerçeveler geri dönmeye başlar.
derinlik(B) = 1 + 0 = 1 döner.
derinlik(E) = 1 + 1 = 2 döner.
derinlik(G) = 1 + 2 = 3 döner. G'nin derinliği 3: kökten G'ye 3 kenar (A–B–E–G). Maliyet O(d + 1).
Şimdi yukseklik(B): aşağı doğru, çocuklar üzerinden özyineleme (derinlik yukarı, yükseklik aşağı bakar).
yukseklik(B): h = 0. Çocuklar: D, E. İlk çocuk D için özyineleme.
yukseklik(D): h = 0; D'nin çocuğu yok, döngü hiç dönmez → return 0 (yaprak).
D'den 0 geldi: h = max(0, 1 + 0) = 1. Sıradaki çocuk: E.
yukseklik(E): h = 0. Çocuklar: G. İlk çocuk G için özyineleme.
yukseklik(G): h = 0; G'nin çocuğu yok, döngü hiç dönmez → return 0 (yaprak).
G'den 0 geldi: h = max(0, 1 + 0) = 1. Çocuk kalmadı → return 1.
E'den 1 geldi: h = max(1, 1 + 1) = 2. Çocuk kalmadı → return 2. Yükseklik(B) = 2 (B–E–G); her düğüm bir kez ziyaret edilir.
int derinlik(Konum<E> p) { if (kokMu(p)) return 0; return 1 + derinlik(ebeveyn(p)); } int yukseklik(Konum<E> p) { int h = 0; for (Konum<E> c : cocuklar(p)) h = Math.max(h, 1 + yukseklik(c)); return h; }
İkili Ağaçlar
İkili Ağaç (Binary Tree)
- Her düğümün en fazla iki çocuğu vardır: sol çocuk ve sağ çocuk.
- Çocukların yeri önemlidir: yalnızca sol çocuğu olan ağaç, yalnızca sağ çocuğu olandan farklıdır.
- Özyinelemeli tanım: İkili ağaç ya boştur ya da bir kök ile iki ikili ağaçtan (sol ve sağ alt ağaç) oluşur.
Soldaki zincir de geçerli bir ikili ağaçtır (çarpık / skewed ağaç): düğümler dengeli dağılmak zorunda değildir.
İkili Ağaçlar
Dolu, Tam ve Mükemmel
İkili Ağaçlar
Hangisi? Dolu mu, Tam mı, Mükemmel mi?
| Dolu (full) | evet |
| Tam (complete) | hayır |
| Mükemmel (perfect) | hayır |
Her düğümün 0 ya da 2 çocuğu var → dolu. Ama C'nin altı boşken en alt seviye E'nin altında → tam değil.
| Dolu (full) | hayır |
| Tam (complete) | evet |
| Mükemmel (perfect) | hayır |
Son seviye (8, 9, 10) soldan sağa boşluksuz → tam. 5'in tek çocuğu (10) var → dolu değil.
| Dolu (full) | hayır |
| Tam (complete) | hayır |
| Mükemmel (perfect) | hayır |
5'in tek çocuğu var → dolu değil. 10. konum boşken 11 dolu → tam değil. Ne tam ne dolu.
| Dolu (full) | evet |
| Tam (complete) | evet |
| Mükemmel (perfect) | evet |
Tüm iç düğümlerin 2 çocuğu var, tüm yapraklar aynı derinlikte → mükemmel (dolayısıyla dolu ve tam).
İkili Ağaçlar
İkili Ağaç Özellikleri
| Özellik | Formül | Örnek |
|---|---|---|
| d derinliğindeki en fazla düğüm | 2d | d = 3 → en fazla 8 düğüm |
| Yüksekliği h olan ağaçta en fazla düğüm | 2h+1 − 1 | h = 3 → 15 (mükemmel ağaç) |
| Yüksekliği h olan ağaçta en az düğüm | h + 1 | çarpık (zincir) ağaç |
| n düğümlü ağacın yüksekliği | ⌊log2 n⌋ ≤ h ≤ n − 1 | n = 15 → 3 ≤ h ≤ 14 |
| n düğümlü ağaçtaki kenar sayısı | n − 1 | her düğümün tek ebeveyni |
| Dolu ikili ağaçta yaprak sayısı | iç düğüm + 1 | 7 düğüm → 3 iç, 4 yaprak |
İkili Ağaçlar · Adım adım
Dolu İkili Ağaçta Neden Yaprak = İç Düğüm + 1?
Tek düğümlü ağaç: kök bir yapraktır (Y). i = 0, y = 1 → y = i + 1.
Bir yaprağa iki çocuk eklendi (dolu kalması için 0 ya da 2 çocuk): o yaprak iç düğüm olur (i + 1), iki yeni yaprak gelir (y − 1 + 2 = y + 1). Fark değişmez.
Bir yaprağa iki çocuk eklendi (dolu kalması için 0 ya da 2 çocuk): o yaprak iç düğüm olur (i + 1), iki yeni yaprak gelir (y − 1 + 2 = y + 1). Fark değişmez.
Bir yaprağa iki çocuk eklendi (dolu kalması için 0 ya da 2 çocuk): o yaprak iç düğüm olur (i + 1), iki yeni yaprak gelir (y − 1 + 2 = y + 1). Fark değişmez.
Her genişletme i'yi de y'yi de 1 artırır; başlangıçta y − i = 1 olduğundan her dolu ikili ağaçta y = i + 1 (tümevarım). Sonuç: n = 2i + 1 → n her zaman tek.
İ: iç düğüm, Y: yaprak; turuncu: son adımda eklenen yapraklar. Bu özellik ifade ağaçlarında (her işleç 2 işlenen) ve Huffman ağaçlarında kullanılır.
Gösterimler
Dizi Gösterimi
Düğümler seviye seviye, soldan sağa diziye yerleştirilir. Kök 1 tabanlı dizide indis 1'de, 0 tabanlıda 0'dadır. İndis i'deki düğüm için:
| 1 tabanlı | 0 tabanlı | |
|---|---|---|
| Sol çocuk | 2i | 2i + 1 |
| Sağ çocuk | 2i + 1 | 2i + 2 |
| Ebeveyn | ⌊i / 2⌋ | ⌊(i − 1) / 2⌋ |
Örnek: E (i = 5) → çocuklar 10 ve 11 (boş), ebeveyn ⌊5/2⌋ = 2 (B).
Referans tutulmaz; ebeveyn/çocuk erişimi O(1) aritmetikle yapılır. Tam ikili ağaçlar için idealdir (ör. heap).
Gösterimler · Kodu adım adım çalıştır
Kod İzleme: Dizi Gösteriminde İndis Hesabı
sol(2) = 2·2 = 4 → a[4] = D, B'nin sol çocuğu (dizi 1 tabanlı, a[0] boş). Referans yok; konum aritmetikle bulunur.
sag(2) = 2·2 + 1 = 5 → a[5] = E. Sağ çocuk her zaman sol çocuğun hemen yanındadır.
ebeveyn(2) = 2 / 2 = 1 → a[1] = A (kök).
D (i = 4): çocuklar 8 → H ve 9 → I.
E (i = 5): 10 ve 11 > n = 9 → dizinin dışında, yani E'nin çocuğu yok (yaprak). Kontrol: 2i ≤ n.
ebeveyn(9) = 9 / 2 = 4: tamsayı bölmesi aşağı yuvarlar (⌊4,5⌋ = 4) → D. 8 ve 9 kardeş olduğundan ikisi de 4'ü verir.
kokeYol(a, 9): I'dan köke kadar ebeveyn zinciri izlenecek.
9 ≥ 1 → döngü sürer; a[9] = I yazdırılır.
i = ebeveyn(9) = 4 → D.
4 ≥ 1 → döngü sürer; a[4] = D yazdırılır.
i = ebeveyn(4) = 2 → B.
2 ≥ 1 → döngü sürer; a[2] = B yazdırılır.
i = ebeveyn(2) = 1 → A.
1 ≥ 1 → döngü sürer; a[1] = A yazdırılır.
i = ebeveyn(1) = 0 (kökün ebeveyni yok).
0 ≥ 1 yanlış → döngü biter. Çıktı: I D B A. Her adım indisi yarıya indirir → O(log n) (derinlik kadar).
int sol(int i) { return 2 * i; } int sag(int i) { return 2 * i + 1; } int ebeveyn(int i) { return i / 2; } void kokeYol(char[] a, int i) { // i'den köke while (i >= 1) { System.out.print(a[i] + " "); i = ebeveyn(i); } }
Gösterimler
Dizide Bellek İsrafı
- Çarpık ağaçta düğümlerin indisleri katlanarak büyür: 1, 2, 4, 8, 16.
- 5 düğüm için 16 hücre gerekir; 11 hücre boş kalır. Sağa çarpık olsaydı indisler 1, 3, 7, 15, 31 olurdu → 31 hücre.
- Genel olarak n düğümlü bir ağaç 2n − 1 hücreye kadar yer isteyebilir.
Dizi gösterimi
- Referans yok, hızlı indis hesabı
- Tam ağaçlarda boşluksuz
Ama
- Seyrek/çarpık ağaçta büyük israf
- Boyut sabit; büyütmek için kopyalama
Gösterimler
Bağlı Gösterim (Linked Representation)
- Her ağaç düğümü bir nesnedir: veri + sol ve sağ çocuk referansı.
- Çocuk yoksa referans
null'dır. - Boyut dinamiktir; yalnızca var olan düğümler için bellek ayrılır.
- Bir düğüme ulaşmak için kökten başlayıp referansları izlemek gerekir.
public class AgacDugumu { int veri; AgacDugumu sol; // sol çocuk (yoksa null) AgacDugumu sag; // sağ çocuk (yoksa null) public AgacDugumu(int veri) { this.veri = veri; // sol = sag = null } }
// 9 kökünde küçük bir ağaç kuralım AgacDugumu kok = new AgacDugumu(9); kok.sol = new AgacDugumu(2); kok.sag = new AgacDugumu(3); kok.sol.sol = new AgacDugumu(4);
Gösterimler
Bağlı Gösterim: Örnek
ebeveyn referansı da eklenir.Gösterimler
Dizi mi, Bağlı mı?
| Ölçüt | Dizi gösterimi | Bağlı gösterim |
|---|---|---|
| Bellek | Tam ağaçta en az; seyrek ağaçta 2n − 1'e kadar israf | Düğüm başına 2 ek referans; israf yok |
| Çocuk / ebeveyn erişimi | O(1), indis aritmetiğiyle | Çocuk O(1); ebeveyn için ek referans gerekir |
| i. seviyedeki k. düğüm | O(1): indis 2i + k − 1 (1 tabanlı, k ≥ 1) | Kökten gezinme gerekir |
| Büyüme | Dizi dolunca kopyalanarak büyütülür | Yeni düğüm eklemek O(1) (konum biliniyorsa) |
| Uygun olduğu yer | Tam ikili ağaçlar (heap) | Genel ve değişken şekilli ağaçlar (BST, ifade ağacı) |
Gösterimler
Genel Ağaç Gösterimi
Çocuk sayısı sınırsız olduğunda iki yaygın yol vardır:
// 1) Çocuk listesi class GenelDugum { int veri; List<GenelDugum> cocuklar = new ArrayList<>(); } // 2) İlk çocuk – sonraki kardeş class KardesDugum { int veri; KardesDugum ilkCocuk; KardesDugum sonrakiKardes; }
İkinci yolda her düğüm yine iki referans tutar: ilk çocuk (aşağı) ve sonraki kardeş (sağa). Böylece her genel ağaç bir ikili ağaç gibi saklanabilir.
Solda genel ağaç; sağda ilk çocuk (düz ok) – sonraki kardeş (kesikli ok) gösterimi.
Gezinmeler
Ağaç Gezinmeleri
| Gezinme | Sıra | Çıktı | Tipik kullanım |
|---|---|---|---|
| Kök başta (preorder) | kök → sol → sağ | 1 2 4 5 3 6 7 | Ağacı kopyalamak, önek ifade |
| Kök ortada (inorder) | sol → kök → sağ | 4 2 5 1 6 3 7 | BST'yi sıralı gezinmek |
| Kök sonda (postorder) | sol → sağ → kök | 4 5 2 6 7 3 1 | Silme, boyut/yükseklik hesabı |
| Seviye sırası (level-order) | seviye seviye, soldan sağa | 1 2 3 4 5 6 7 | Seviye bazlı işlemler |
Gezinmeler
Kök Başta Gezinme (Preorder)
- Kökü ziyaret et.
- Sol alt ağacı kök başta gez.
- Sağ alt ağacı kök başta gez.
Kullanım: ağacı kopyalamak, düğümleri diziye yazmak, önek (prefix) ifade.
ziyaret(kok); kokBasta(kok.sol); kokBasta(kok.sag);
Kök 1 ziyaret edilir; sonra sol alt ağaca geçilir.
2 ziyaret edilir; sonra onun sol alt ağacı.
4 yaprak: ziyaret edilir, çocuğu yok.
2'nin sağ alt ağacı: 5.
Sol alt ağaç bitti; 1'in sağ alt ağacı: 3.
3'ün sol çocuğu 6.
3'ün sağ çocuğu 7. Çıktı: 1 2 4 5 3 6 7
Gezinmeler
Kök Ortada Gezinme (Inorder)
- Sol alt ağacı kök ortada gez.
- Kökü ziyaret et.
- Sağ alt ağacı kök ortada gez.
Kullanım: ikili arama ağacında anahtarları küçükten büyüğe sıralı vermek, ara ek (infix) ifade.
kokOrtada(kok.sol); ziyaret(kok); kokOrtada(kok.sag);
Kökten hep sola inilir; ilk ziyaret en soldaki düğüm: 4.
4'ün alt ağacı bitti → ebeveyni 2.
2'nin sağ alt ağacı: 5.
Sol alt ağaç tamamlandı → kök 1.
Sağ alt ağaçta yine en sola inilir: 6.
6'dan sonra ebeveyni 3.
Son olarak 7. Çıktı: 4 2 5 1 6 3 7
Gezinmeler
Kök Sonda Gezinme (Postorder)
- Sol alt ağacı kök sonda gez.
- Sağ alt ağacı kök sonda gez.
- Kökü ziyaret et.
Kullanım: ağacı silmek (önce çocuklar), boyut/yükseklik hesabı, sonek (postfix) ifade.
kokSonda(kok.sol); kokSonda(kok.sag); ziyaret(kok);
En soldaki yaprak 4 ilk ziyaret edilir.
Sonra 2'nin sağ çocuğu 5.
2'nin iki alt ağacı da bitti → 2.
1'in sağ alt ağacı: önce 6.
Sonra 7.
3'ün çocukları bitti → 3.
Kök en son ziyaret edilir: 1. Çıktı: 4 5 2 6 7 3 1
Gezinmeler · Adım adım
Üç Gezinme, Tek Tur: Euler Turu
Ağacın etrafında, kökün solundan başlayıp kenarlara sol elimizle dokunarak yürüyelim (Euler turu). Her düğüme üç kez uğranır: solundan, altından, sağından.
Bir düğümü soldan geçerken yazarsak (ilk karşılaşma) kök başta sırası çıkar: A B D E C.
Altından geçerken (sol alt ağaç bitmiş, sağ başlamamış) yazarsak kök ortada sırası çıkar: D B E A C.
Sağından geçerken (iki alt ağaç da bitmiş) yazarsak kök sonda sırası çıkar: D E B C A. Üç gezinme aynı turun farklı anlarıdır; tur O(n) sürer.
Gezinmeler
Seviye Sırası Gezinme (Level-order)
- Kökten başlar, her seviyeyi soldan sağa tarar, sonra bir alt seviyeye iner.
- Genişlik öncelikli aramanın (BFS) ağaçtaki hâlidir.
- Özyineleme yerine bir kuyruk (FIFO) kullanır: önce gelen düğümün çocukları önce işlenir.
Kullanım: seviye bazlı işlemler (her seviyenin toplamı, en yakın yaprak), ağacı seviye seviye yazdırmak.
Gezinmeler · Kodu adım adım çalıştır
Özyinelemeli Kök Başta ve Çağrı Yığını
kokBasta(9) çağrıldı (kök). null değil → önce kök yazdırılır: 9, sonra alt ağaçlar.
kokBasta(2) çağrıldı (9'un sol çocuğu). null değil → önce kök yazdırılır: 2, sonra alt ağaçlar.
kokBasta(4) çağrıldı (2'nin sol çocuğu). null değil → önce kök yazdırılır: 4, sonra alt ağaçlar.
kokBasta(4.sol) = kokBasta(null): taban durum, hemen return. Boş alt ağaçta yapılacak iş yok.
kokBasta(4.sag) = kokBasta(null): taban durum, hemen return. Boş alt ağaçta yapılacak iş yok.
kokBasta(4) bitti (satır 8); çerçeve yığından çıkar, kokBasta(2)'ye dönülür.
kokBasta(2.sag) = kokBasta(null): taban durum, hemen return. Boş alt ağaçta yapılacak iş yok.
kokBasta(2) bitti (satır 8); çerçeve yığından çıkar, kokBasta(9)'a dönülür.
kokBasta(3) çağrıldı (9'un sağ çocuğu). null değil → önce kök yazdırılır: 3, sonra alt ağaçlar.
kokBasta(3.sol) = kokBasta(null): taban durum, hemen return. Boş alt ağaçta yapılacak iş yok.
kokBasta(3.sag) = kokBasta(null): taban durum, hemen return. Boş alt ağaçta yapılacak iş yok.
kokBasta(3) bitti (satır 8); çerçeve yığından çıkar, kokBasta(9)'a dönülür.
kokBasta(9) bitti (satır 8); yığın boşalır. Çıktı: 9 2 4 3 — her düğüm bir kez, en fazla h + 2 = 4 çerçeve.
public void kokBasta(AgacDugumu kok) { if (kok == null) { return; } System.out.print(kok.veri + " "); kokBasta(kok.sol); kokBasta(kok.sag); }
Gezinmeler · Kodu adım adım çalıştır
Özyinelemeli Kök Ortada ve Çağrı Yığını
kokOrtada(9) çağrıldı (kök). 9 henüz yazdırılmaz: önce sol alt ağaç gezilmeli.
kokOrtada(2) çağrıldı (9'un sol çocuğu). 2 henüz yazdırılmaz: önce sol alt ağaç gezilmeli.
kokOrtada(4) çağrıldı (2'nin sol çocuğu). 4 henüz yazdırılmaz: önce sol alt ağaç gezilmeli.
kokOrtada(4.sol) = kokOrtada(null): taban durum, hemen return. Boş alt ağaçta yapılacak iş yok.
Sol alt ağaç bitti, kokOrtada(4)'e dönüldü → 4 yazdırılır. Sırada sağ alt ağaç var.
kokOrtada(4.sag) = kokOrtada(null): taban durum, hemen return. Boş alt ağaçta yapılacak iş yok.
kokOrtada(4) bitti (satır 8); çerçeve yığından çıkar, kokOrtada(2)'ye dönülür.
Sol alt ağaç bitti, kokOrtada(2)'ye dönüldü → 2 yazdırılır. Sırada sağ alt ağaç var.
kokOrtada(2.sag) = kokOrtada(null): taban durum, hemen return. Boş alt ağaçta yapılacak iş yok.
kokOrtada(2) bitti (satır 8); çerçeve yığından çıkar, kokOrtada(9)'a dönülür.
Sol alt ağaç bitti, kokOrtada(9)'a dönüldü → 9 yazdırılır. Sırada sağ alt ağaç var.
kokOrtada(3) çağrıldı (9'un sağ çocuğu). 3 henüz yazdırılmaz: önce sol alt ağaç gezilmeli.
kokOrtada(3.sol) = kokOrtada(null): taban durum, hemen return. Boş alt ağaçta yapılacak iş yok.
Sol alt ağaç bitti, kokOrtada(3)'e dönüldü → 3 yazdırılır. Sırada sağ alt ağaç var.
kokOrtada(3.sag) = kokOrtada(null): taban durum, hemen return. Boş alt ağaçta yapılacak iş yok.
kokOrtada(3) bitti (satır 8); çerçeve yığından çıkar, kokOrtada(9)'a dönülür.
kokOrtada(9) bitti (satır 8); yığın boşalır. Çıktı: 4 2 9 3 — her düğüm bir kez, en fazla h + 2 = 4 çerçeve.
public void kokOrtada(AgacDugumu kok) { if (kok == null) { return; } kokOrtada(kok.sol); System.out.print(kok.veri + " "); kokOrtada(kok.sag); }
Gezinmeler · Kodu adım adım çalıştır
Özyinelemeli Kök Sonda ve Çağrı Yığını
kokSonda(9) çağrıldı (kök). Kök en sona kalır: önce sol alt ağaç.
kokSonda(2) çağrıldı (9'un sol çocuğu). Kök en sona kalır: önce sol alt ağaç.
kokSonda(4) çağrıldı (2'nin sol çocuğu). Kök en sona kalır: önce sol alt ağaç.
kokSonda(4.sol) = kokSonda(null): taban durum, hemen return. Boş alt ağaçta yapılacak iş yok.
kokSonda(4.sag) = kokSonda(null): taban durum, hemen return. Boş alt ağaçta yapılacak iş yok.
kokSonda(4): iki alt ağaç da bitti → şimdi 4 yazdırılır.
kokSonda(4) bitti (satır 8); çerçeve yığından çıkar, kokSonda(2)'ye dönülür.
kokSonda(2.sag) = kokSonda(null): taban durum, hemen return. Boş alt ağaçta yapılacak iş yok.
kokSonda(2): iki alt ağaç da bitti → şimdi 2 yazdırılır.
kokSonda(2) bitti (satır 8); çerçeve yığından çıkar, kokSonda(9)'a dönülür.
kokSonda(3) çağrıldı (9'un sağ çocuğu). Kök en sona kalır: önce sol alt ağaç.
kokSonda(3.sol) = kokSonda(null): taban durum, hemen return. Boş alt ağaçta yapılacak iş yok.
kokSonda(3.sag) = kokSonda(null): taban durum, hemen return. Boş alt ağaçta yapılacak iş yok.
kokSonda(3): iki alt ağaç da bitti → şimdi 3 yazdırılır.
kokSonda(3) bitti (satır 8); çerçeve yığından çıkar, kokSonda(9)'a dönülür.
kokSonda(9): iki alt ağaç da bitti → şimdi 9 yazdırılır.
kokSonda(9) bitti (satır 8); yığın boşalır. Çıktı: 4 2 3 9 — her düğüm bir kez, en fazla h + 2 = 4 çerçeve.
public void kokSonda(AgacDugumu kok) { if (kok == null) { return; } kokSonda(kok.sol); kokSonda(kok.sag); System.out.print(kok.veri + " "); }
Gezinmeler · Kodu adım adım çalıştır
Yığın ile Kök Başta Gezinme
Kök yığına itilir. Yığın, özyinelemedeki çağrı yığınının yerini tutar.
pop() üstteki 1'i verir; hemen yazdırılır (kök başta: önce kök).
Sağ çocuk 3 itilir. Önce sağ itilir ki sol üstte kalıp önce işlensin (LIFO).
Sol çocuk 2 itilir → yığının üstünde; bir sonraki pop() onu verir.
pop() üstteki 2'yi verir; hemen yazdırılır (kök başta: önce kök).
Sağ çocuk 5 itilir. Önce sağ itilir ki sol üstte kalıp önce işlensin (LIFO).
Sol çocuk 4 itilir → yığının üstünde; bir sonraki pop() onu verir.
pop() üstteki 4'ü verir; hemen yazdırılır (kök başta: önce kök). 4 yaprak: itilecek çocuk yok.
pop() üstteki 5'i verir; hemen yazdırılır (kök başta: önce kök). 5 yaprak: itilecek çocuk yok.
pop() üstteki 3'ü verir; hemen yazdırılır (kök başta: önce kök).
Sağ çocuk 7 itilir. Önce sağ itilir ki sol üstte kalıp önce işlensin (LIFO).
Sol çocuk 6 itilir → yığının üstünde; bir sonraki pop() onu verir.
pop() üstteki 6'yı verir; hemen yazdırılır (kök başta: önce kök). 6 yaprak: itilecek çocuk yok.
pop() üstteki 7'yi verir; hemen yazdırılır (kök başta: önce kök). 7 yaprak: itilecek çocuk yok.
Yığın boş → döngü biter. Çıktı: 1 2 4 5 3 6 7 — özyinelemeli kök başta ile aynı.
public void kokBastaYigin(AgacDugumu kok) { if (kok == null) return; Deque<AgacDugumu> yigin = new ArrayDeque<>(); yigin.push(kok); while (!yigin.isEmpty()) { AgacDugumu gecici = yigin.pop(); System.out.print(gecici.veri + " "); if (gecici.sag != null) yigin.push(gecici.sag); // önce sağ if (gecici.sol != null) yigin.push(gecici.sol); // sol üstte } }
Gezinmeler · Kodu adım adım çalıştır
Yığın ile Kök Ortada Gezinme
gecici kökten başlar; yığın boş.
1 itilir (sonra yazdırılacak), gecici = 1.sol = 2. Sola inmeye devam.
2 itilir (sonra yazdırılacak), gecici = 2.sol = 4. Sola inmeye devam.
4 itilir (sonra yazdırılacak), gecici = 4.sol = null. Sola gidilecek yer kalmadı.
gecici null → 4 çıkarılıp yazdırılır (solundakilerin hepsi bitti). gecici = 4.sag = null.
gecici null → 2 çıkarılıp yazdırılır (solundakilerin hepsi bitti). gecici = 2.sag = 5.
5 itilir (sonra yazdırılacak), gecici = 5.sol = null. Sola gidilecek yer kalmadı.
gecici null → 5 çıkarılıp yazdırılır (solundakilerin hepsi bitti). gecici = 5.sag = null.
gecici null → 1 çıkarılıp yazdırılır (solundakilerin hepsi bitti). gecici = 1.sag = 3.
3 itilir (sonra yazdırılacak), gecici = 3.sol = 6. Sola inmeye devam.
6 itilir (sonra yazdırılacak), gecici = 6.sol = null. Sola gidilecek yer kalmadı.
gecici null → 6 çıkarılıp yazdırılır (solundakilerin hepsi bitti). gecici = 6.sag = null.
gecici null → 3 çıkarılıp yazdırılır (solundakilerin hepsi bitti). gecici = 3.sag = null.
Yığın boş ve gecici null → döngü biter. Çıktı: 4 2 5 1 6 3 (sol → kök → sağ).
public void kokOrtadaYigin(AgacDugumu kok) { if (kok == null) return; Deque<AgacDugumu> yigin = new ArrayDeque<>(); AgacDugumu gecici = kok; while (!yigin.isEmpty() || gecici != null) { if (gecici != null) { yigin.push(gecici); gecici = gecici.sol; } else { gecici = yigin.pop(); System.out.print(gecici.veri + " "); gecici = gecici.sag; } } }
Gezinmeler
Yığın ile Kök Sonda Gezinme: Kod
public void kokSondaYigin(AgacDugumu kok) { AgacDugumu simdiki = kok; Deque<AgacDugumu> yigin = new ArrayDeque<>(); while (simdiki != null || !yigin.isEmpty()) { if (simdiki != null) { yigin.push(simdiki); simdiki = simdiki.sol; } else { AgacDugumu gecici = yigin.peek().sag; if (gecici == null) { gecici = yigin.pop(); System.out.print(gecici.veri + " "); while (!yigin.isEmpty() && gecici == yigin.peek().sag) { gecici = yigin.pop(); System.out.print(gecici.veri + " "); } } else { simdiki = gecici; } } } }
- Kök, iki alt ağacı da bittikten sonra yazdırılmalı; bu yüzden yığındaki düğüm hemen çıkarılmaz, önce
peekile bakılır. - Üstteki düğümün sağ çocuğu varsa önce o alt ağaca gidilir.
- Sağ çocuk yoksa düğüm yazdırılır. Az önce yazdırılan düğüm, yığındakinin sağ çocuğuysa o ebeveynin de işi bitmiştir → o da çıkarılıp yazdırılır.
Gezinmeler · Kodu adım adım çalıştır
Yığın ile Kök Sonda Gezinme: İzleme
simdiki kökten başlar, yığın boş.
1 itilir, simdiki = 1.sol = 2.
2 itilir, simdiki = 2.sol = 4.
4 itilir, simdiki = 4.sol = null.
peek() = 4, sağ çocuğu yok → iki alt ağacı da bitmiş sayılır: 4 çıkarılıp yazdırılır.
4, üstteki 2'nin sağ çocuğu değil (2.sag = 5) → iç döngü durur; 2'nin sağ alt ağacına henüz girilmedi.
peek() = 2; sağ çocuğu 5 var → 2 henüz yazdırılamaz, önce simdiki = 5 ile sağ alt ağaca gidilir.
5 itilir, simdiki = 5.sol = null.
peek() = 5, sağ çocuğu yok → iki alt ağacı da bitmiş sayılır: 5 çıkarılıp yazdırılır.
Az önce yazdırılan düğüm, 2'nin sağ çocuğuydu → 2'nin sağ alt ağacı da bitti: 2 çıkarılıp yazdırılır.
2, üstteki 1'in sağ çocuğu değil (1.sag = 3) → iç döngü durur; 1'in sağ alt ağacına henüz girilmedi.
peek() = 1; sağ çocuğu 3 var → 1 henüz yazdırılamaz, önce simdiki = 3 ile sağ alt ağaca gidilir.
3 itilir, simdiki = 3.sol = null.
peek() = 3; sağ çocuğu 6 var → 3 henüz yazdırılamaz, önce simdiki = 6 ile sağ alt ağaca gidilir.
6 itilir, simdiki = 6.sol = null.
peek() = 6, sağ çocuğu yok → iki alt ağacı da bitmiş sayılır: 6 çıkarılıp yazdırılır.
Az önce yazdırılan düğüm, 3'ün sağ çocuğuydu → 3'ün sağ alt ağacı da bitti: 3 çıkarılıp yazdırılır.
Az önce yazdırılan düğüm, 1'in sağ çocuğuydu → 1'in sağ alt ağacı da bitti: 1 çıkarılıp yazdırılır.
Yığın boşaldı → iç döngü durur.
simdiki null ve yığın boş → biter. Çıktı: 4 5 2 6 3 1 (sol → sağ → kök).
public void kokSondaYigin(AgacDugumu kok) { AgacDugumu simdiki = kok; Deque<AgacDugumu> yigin = new ArrayDeque<>(); while (simdiki != null || !yigin.isEmpty()) { if (simdiki != null) { yigin.push(simdiki); simdiki = simdiki.sol; } else { AgacDugumu gecici = yigin.peek().sag; if (gecici == null) { gecici = yigin.pop(); System.out.print(gecici.veri + " "); while (!yigin.isEmpty() && gecici == yigin.peek().sag) { gecici = yigin.pop(); System.out.print(gecici.veri + " "); } } else { simdiki = gecici; } } } }
Gezinmeler · Kodu adım adım çalıştır
Kuyruk ile Seviye Sırası Gezinme
Kök kuyruğa eklenir. Kuyruk FIFO: önce eklenen önce çıkar.
poll() öndeki 4'ü verir ve 4 yazdırılır.
Sol çocuk 3 kuyruğun arkasına eklenir; kendi seviyesindekilerden sonra işlenecek.
Sağ çocuk 7 arkaya eklenir.
poll() öndeki 3'ü verir ve 3 yazdırılır.
Sol çocuk 5 kuyruğun arkasına eklenir; kendi seviyesindekilerden sonra işlenecek.
Sağ çocuk 8 arkaya eklenir.
poll() öndeki 7'yi verir ve 7 yazdırılır. Yaprak: eklenecek çocuk yok.
poll() öndeki 5'i verir ve 5 yazdırılır. Yaprak: eklenecek çocuk yok.
poll() öndeki 8'i verir ve 8 yazdırılır. Yaprak: eklenecek çocuk yok.
Kuyruk boş → döngü biter. Çıktı: 4 3 7 5 8 — seviye seviye (kök başta olsaydı: 4 3 5 8 7).
public void seviyeSirasi(AgacDugumu kok) { if (kok == null) return; Queue<AgacDugumu> kuyruk = new LinkedList<>(); kuyruk.add(kok); while (!kuyruk.isEmpty()) { AgacDugumu gecici = kuyruk.poll(); System.out.print(gecici.veri + " "); if (gecici.sol != null) kuyruk.add(gecici.sol); if (gecici.sag != null) kuyruk.add(gecici.sag); } }
Gezinmeler
Uygulama: İfade Ağacı
- İç düğümler işleçler, yapraklar işlenenlerdir.
- Aynı ağaç, gezinme türüne göre farklı gösterimler verir.
- Değer, kök sonda gezinmeyle hesaplanır: önce iki alt ağacın değeri, sonra işleç.
| Gezinme | Çıktı | Gösterim |
|---|---|---|
| Kök başta | × + 3 4 − 5 2 | önek (prefix) |
| Kök ortada | 3 + 4 × 5 − 2 → parantezle: (3 + 4) × (5 − 2) | ara ek (infix) |
| Kök sonda | 3 4 + 5 2 − × → 7 × 3 = 21 | sonek (postfix) |
Kök ortada çıktısında parantez kaybolur; ifadeyi doğru yazmak için her alt ağaç parantez içine alınmalıdır.
Gezinmeler · Kodu adım adım çalıştır
İfade Ağacını Hesaplama: hesapla()
hesapla(×): iç düğüm (işleç). İşleci uygulamadan önce sol işlenenin değeri gerekir → hesapla(+).
hesapla(+): iç düğüm (işleç). İşleci uygulamadan önce sol işlenenin değeri gerekir → hesapla(3).
hesapla(3): çocuğu yok → yaprak, sayıyı döndürür: 3.
a = 3 geldi. Şimdi sağ işlenen: hesapla(4).
hesapla(4): çocuğu yok → yaprak, sayıyı döndürür: 4.
b = 4. İki alt ağaç hazır → işleç uygulanır: 3 + 4 = 7 döner (kök sonda mantığı).
a = 7 geldi. Şimdi sağ işlenen: hesapla(−).
hesapla(−): iç düğüm (işleç). İşleci uygulamadan önce sol işlenenin değeri gerekir → hesapla(5).
hesapla(5): çocuğu yok → yaprak, sayıyı döndürür: 5.
a = 5 geldi. Şimdi sağ işlenen: hesapla(2).
hesapla(2): çocuğu yok → yaprak, sayıyı döndürür: 2.
b = 2. İki alt ağaç hazır → işleç uygulanır: 5 − 2 = 3 döner (kök sonda mantığı).
b = 3. İki alt ağaç hazır → işleç uygulanır: 7 × 3 = 21 döner (kök sonda mantığı). (3 + 4) × (5 − 2) = 21
int hesapla(IfadeDugumu d) { if (d.sol == null && d.sag == null) return d.sayi; // yaprak: işlenen int a = hesapla(d.sol); int b = hesapla(d.sag); switch (d.islec) { // iç düğüm: işleç case '+': return a + b; case '-': return a - b; case '*': return a * b; default: return a / b; } }
Gezinmeler · Kodu adım adım çalıştır
İkili Ağaçta En Büyük Değeri Bulma
enBuyukBul(4): sonuc = 4; sol alt ağacın en büyüğü sorulur.
enBuyukBul(3): sonuc = 3; sol alt ağacın en büyüğü sorulur.
enBuyukBul(5): sonuc = 5; sol alt ağacın en büyüğü sorulur.
enBuyukBul(5.sol): boş alt ağaç → Integer.MIN_VALUE döner; hiçbir değerden büyük olamaz, karşılaştırmayı bozmaz.
Sol sonuç MIN geldi (sol = MIN); şimdi sağ alt ağaç.
enBuyukBul(5.sag): boş alt ağaç → Integer.MIN_VALUE döner; hiçbir değerden büyük olamaz, karşılaştırmayı bozmaz.
Karşılaştırmalar: sol = MIN ≤ 5; sag = MIN ≤ 5. return 5.
Sol sonuç 5 geldi (sol = 5); şimdi sağ alt ağaç.
enBuyukBul(8): sonuc = 8; sol alt ağacın en büyüğü sorulur.
enBuyukBul(8.sol): boş alt ağaç → Integer.MIN_VALUE döner; hiçbir değerden büyük olamaz, karşılaştırmayı bozmaz.
Sol sonuç MIN geldi (sol = MIN); şimdi sağ alt ağaç.
enBuyukBul(8.sag): boş alt ağaç → Integer.MIN_VALUE döner; hiçbir değerden büyük olamaz, karşılaştırmayı bozmaz.
Karşılaştırmalar: sol = MIN ≤ 8; sag = MIN ≤ 8. return 8.
Karşılaştırmalar: sol = 5 > 3 → sonuc = 5; sag = 8 > 5 → sonuc = 8. return 8.
Sol sonuç 8 geldi (sol = 8); şimdi sağ alt ağaç.
enBuyukBul(7): sonuc = 7; sol alt ağacın en büyüğü sorulur.
enBuyukBul(7.sol): boş alt ağaç → Integer.MIN_VALUE döner; hiçbir değerden büyük olamaz, karşılaştırmayı bozmaz.
Sol sonuç MIN geldi (sol = MIN); şimdi sağ alt ağaç.
enBuyukBul(7.sag): boş alt ağaç → Integer.MIN_VALUE döner; hiçbir değerden büyük olamaz, karşılaştırmayı bozmaz.
Karşılaştırmalar: sol = MIN ≤ 7; sag = MIN ≤ 7. return 7.
Karşılaştırmalar: sol = 8 > 4 → sonuc = 8; sag = 7 ≤ 8. return 8. Sonuç: 8
public int enBuyukBul(AgacDugumu kok) { if (kok == null) { return Integer.MIN_VALUE; } int sonuc = kok.veri; int sol = enBuyukBul(kok.sol); int sag = enBuyukBul(kok.sag); if (sol > sonuc) sonuc = sol; if (sag > sonuc) sonuc = sag; return sonuc; }
Gezinmeler
Özyinelemeli Ağaç Fonksiyonları
int boyut(AgacDugumu k) { // düğüm sayısı if (k == null) return 0; return 1 + boyut(k.sol) + boyut(k.sag); } int yukseklik(AgacDugumu k) { // kenar sayısı if (k == null) return -1; // boş ağaç: -1 return 1 + Math.max(yukseklik(k.sol), yukseklik(k.sag)); } int yaprakSayisi(AgacDugumu k) { if (k == null) return 0; if (k.sol == null && k.sag == null) return 1; return yaprakSayisi(k.sol) + yaprakSayisi(k.sag); }
Gezinmeler · Kodu adım adım çalıştır
Kod İzleme: boyut() — değerler yukarı taşınır
boyut(9) (kök): null değil → önce sol alt ağaç için boyut(2) çağrılır.
boyut(2) (9'un sol çocuğu): null değil → önce sol alt ağaç için boyut(4) çağrılır.
boyut(4) (2'nin sol çocuğu): null değil → önce sol alt ağaç için boyut(null) çağrılır.
boyut(4.sol) = boyut(null): boş ağaç → return 0.
Sol sonuç 0 geldi. Şimdi sağ alt ağaç: boyut(null).
boyut(4.sag) = boyut(null): boş ağaç → return 0.
boyut(4) = 1 + 0 + 0 = 1 döner. Değer 2'nin çerçevesine taşınır.
Sol sonuç 1 geldi. Şimdi sağ alt ağaç: boyut(null).
boyut(2.sag) = boyut(null): boş ağaç → return 0.
boyut(2) = 1 + 1 + 0 = 2 döner. Değer 9'un çerçevesine taşınır.
Sol sonuç 2 geldi. Şimdi sağ alt ağaç: boyut(3).
boyut(3) (9'un sağ çocuğu): null değil → önce sol alt ağaç için boyut(null) çağrılır.
boyut(3.sol) = boyut(null): boş ağaç → return 0.
Sol sonuç 0 geldi. Şimdi sağ alt ağaç: boyut(null).
boyut(3.sag) = boyut(null): boş ağaç → return 0.
boyut(3) = 1 + 0 + 0 = 1 döner. Değer 9'un çerçevesine taşınır.
boyut(9) = 1 + 2 + 1 = 4 döner. Sonuç: 4
int boyut(AgacDugumu k) { // düğüm sayısı if (k == null) return 0; return 1 + boyut(k.sol) + boyut(k.sag); }
Gezinmeler · Kodu adım adım çalıştır
Kod İzleme: yukseklik()
yukseklik(9) (kök): null değil → önce sol alt ağaç için yukseklik(2) çağrılır.
yukseklik(2) (9'un sol çocuğu): null değil → önce sol alt ağaç için yukseklik(4) çağrılır.
yukseklik(4) (2'nin sol çocuğu): null değil → önce sol alt ağaç için yukseklik(null) çağrılır.
yukseklik(4.sol) = yukseklik(null): boş ağaç → return -1. Boş ağacın yüksekliği −1 kabul edilir; böylece yaprak 1 + max(−1, −1) = 0 olur.
Sol sonuç -1 geldi. Şimdi sağ alt ağaç: yukseklik(null).
yukseklik(4.sag) = yukseklik(null): boş ağaç → return -1.
yukseklik(4) = 1 + max(-1, -1) = 0 döner. Değer 2'nin çerçevesine taşınır.
Sol sonuç 0 geldi. Şimdi sağ alt ağaç: yukseklik(null).
yukseklik(2.sag) = yukseklik(null): boş ağaç → return -1.
yukseklik(2) = 1 + max(0, -1) = 1 döner. Değer 9'un çerçevesine taşınır.
Sol sonuç 1 geldi. Şimdi sağ alt ağaç: yukseklik(3).
yukseklik(3) (9'un sağ çocuğu): null değil → önce sol alt ağaç için yukseklik(null) çağrılır.
yukseklik(3.sol) = yukseklik(null): boş ağaç → return -1.
Sol sonuç -1 geldi. Şimdi sağ alt ağaç: yukseklik(null).
yukseklik(3.sag) = yukseklik(null): boş ağaç → return -1.
yukseklik(3) = 1 + max(-1, -1) = 0 döner. Değer 9'un çerçevesine taşınır.
yukseklik(9) = 1 + max(1, 0) = 2 döner. Sonuç: 2
int yukseklik(AgacDugumu k) { // kenar sayısı if (k == null) return -1; // boş ağaç: -1 return 1 + Math.max(yukseklik(k.sol), yukseklik(k.sag)); }
Gezinmeler · Kodu adım adım çalıştır
Kod İzleme: yaprakSayisi()
yaprakSayisi(9) (kök): yaprak değil → sol ve sağ alt ağaçların yaprakları toplanacak; önce sol.
yaprakSayisi(2) (9'un sol çocuğu): yaprak değil → sol ve sağ alt ağaçların yaprakları toplanacak; önce sol.
yaprakSayisi(4) (2'nin sol çocuğu): iki çocuğu da null → yaprak, return 1. Alt çağrı yapılmaz.
yaprakSayisi(2.sag) = yaprakSayisi(null): boş ağaç → return 0.
yaprakSayisi(2) = 1 + 0 = 1 döner.
yaprakSayisi(3) (9'un sağ çocuğu): iki çocuğu da null → yaprak, return 1. Alt çağrı yapılmaz.
yaprakSayisi(9) = 1 + 1 = 2 döner. Sonuç: 2
int yaprakSayisi(AgacDugumu k) { if (k == null) return 0; if (k.sol == null && k.sag == null) return 1; return yaprakSayisi(k.sol) + yaprakSayisi(k.sag); }
Gezinmeler · Kodu adım adım çalıştır
Kod İzleme: kök başta + kök ortada'dan ağacı kurma
pre[0] = 1 → yeni düğüm (kök). in'de m = 3: [0..2] sol alt ağaç, [4..5] sağ alt ağaç olur.
pre[1] = 2 → yeni düğüm (1'in sol çocuğu). in'de m = 1: [0..0] sol alt ağaç, [2..2] sağ alt ağaç olur.
pre[2] = 4 → yeni düğüm (2'nin sol çocuğu); m = 0. Sol ve sağ aralık boş → iki çağrı null döner: 4 yaprak.
pre[3] = 5 → yeni düğüm (2'nin sağ çocuğu); m = 2. Sol ve sağ aralık boş → iki çağrı null döner: 5 yaprak.
2'nin alt ağaçları kuruldu → return düğüm(2); 1.sol = 2 yapılır.
pre[4] = 3 → yeni düğüm (1'in sağ çocuğu). in'de m = 4: [4..3] sol alt ağaç, [5..5] sağ alt ağaç olur.
Sol aralık [4..3] boş → kur hemen null döner: 3'ün sol çocuğu yok.
pre[5] = 6 → yeni düğüm (3'ün sağ çocuğu); m = 5. Sol ve sağ aralık boş → iki çağrı null döner: 6 yaprak.
3'ün alt ağaçları kuruldu → return düğüm(3); 1.sag = 3 yapılır.
1'in alt ağaçları kuruldu → return düğüm(1); kök olarak kok'a atanır. Sonuç: 1(2(4, 5), 3(—, 6)). m araması yüzünden O(n2); in[] indisleri bir eşlemede (map) tutulursa O(n).
int p = 0; // pre[] içinde sıradaki kök AgacDugumu kur(int[] pre, int[] in, int bas, int son) { if (bas > son) return null; // boş aralık AgacDugumu k = new AgacDugumu(pre[p++]); int m = bas; while (in[m] != k.veri) m++; k.sol = kur(pre, in, bas, m - 1); // soldakiler k.sag = kur(pre, in, m + 1, son); // sağdakiler return k; }
Gezinmeler
Gezinmelerin Maliyeti
| Yöntem | Zaman | Ek bellek | Neden? |
|---|---|---|---|
| Özyinelemeli pre/in/post | O(n) | O(h) | Çağrı yığınında en fazla h + 2 çerçeve (null çağrısı dahil) |
| Yığın ile pre/in/post | O(n) | O(h) | Açık yığın, özyinelemenin yerini tutar |
| Kuyruk ile seviye sırası | O(n) | O(w) | w: en geniş seviyedeki düğüm sayısı |
StackOverflowError verebilir → yığınlı sürüm tercih edilir.Özet
Özet
| Konu | Hatırlanacaklar |
|---|---|
| Terimler | kök, ebeveyn/çocuk, yaprak, iç düğüm; n düğüm → n − 1 kenar |
| Ölçüler | derece = çocuk sayısı; derinlik = kökten kenar sayısı; yükseklik = en uzun aşağı yol |
| İkili ağaç | dolu: 0/2 çocuk · tam: son seviye soldan dolar · mükemmel: tüm seviyeler dolu |
| Gösterim | dizi: indis bağıntıları · bağlı: sol/sağ referansları |
| Gezinme | kök başta K-S-S · kök ortada S-K-S · kök sonda S-S-K · seviye sırası kuyrukla |
| Maliyet | tüm gezinmeler O(n); ek bellek O(h) (yığın) veya O(w) (kuyruk) |
Özet
Etkileşimli Simülatörler
Özet
Sık Yapılan Hatalar
k.sol'a erişmeden önce k == null denetlenmezse NullPointerException. Her özyinelemeli ağaç metodu boş ağaçla başlar.StackOverflowError verir → yığınlı ya da kuyruklu sürüm.Özet
Örnek Problemler
- Sağdaki ağacın kök başta, kök ortada, kök sonda ve seviye sırası çıktılarını yazın.
- Ağacın yüksekliği nedir? Yapraklar hangileri? E'nin derinliği kaçtır?
- Bu ağaç dolu mu, tam mı, mükemmel mi?
- 1 tabanlı dizi gösteriminde F ve G hangi indislerde durur? Dizi en az kaç hücre olmalı?
- Yüksekliği 4 olan bir ikili ağaçta en fazla kaç düğüm olabilir?
- Kök başta: A B D E C F, kök ortada: D B E A C F. Ağacı çizin.
Cevapları göster
- Kök başta: A B D E G C F · kök ortada: D B G E A C F · kök sonda: D G E B F C A · seviye: A B C D E F G
- Yükseklik 3 (A→B→E→G); yapraklar D, G, F; E'nin derinliği 2.
- Hiçbiri: E ve C'nin tek çocuğu var (dolu değil); 6. konum boşken 7 dolu (tam değil).
- F: C'nin sağ çocuğu → 2·3 + 1 = 7; G: E'nin sol çocuğu → 2·5 = 10. En az 10 hücre (1..10).
- 25 − 1 = 31.
- A kök; sol alt ağaç {D, B, E}: B kök, D sol, E sağ; sağ alt ağaç {C, F}: C kök, F sağ çocuk.
Özet
Alıştırmalar
int icDugumSayisi(AgacDugumu k): iç düğümleri sayan metodu yazın.void ayna(AgacDugumu k): ağacın aynadaki görüntüsünü (her düğümde sol ↔ sağ) oluşturun.boolean esitMi(AgacDugumu a, AgacDugumu b): iki ağaç şekil ve değerce aynı mı?int seviyedeki(AgacDugumu k, int d): d derinliğindeki düğüm sayısını bulun.- Kök sonda: D E B F C A, kök ortada: D B E A F C. Ağacı çizin.
- n düğümlü dolu ikili ağaçta kaç yaprak vardır? n çift olabilir mi?
Cevaplar / ipuçları
nullya da yaprak → 0; değilse 1 + sol + sağ. O(n).- Kök sonda:
ayna(k.sol); ayna(k.sag);sonrak.solilek.sag'ı takas et. - İkisi de null → true; biri null → false; değilse veri eşit && sollar eşit && sağlar eşit.
d == 0→ 1; değilseseviyedeki(k.sol, d−1) + seviyedeki(k.sag, d−1); null → 0.- Kök A (son); sol {D, B, E}: B(D, E); sağ {F, C}: C, F sol çocuk → A(B(D, E), C(F, —)).
- y = i + 1 ve n = i + y → y = (n + 1)/2; n = 2i + 1 hep tektir.
Özet
Kaynaklar
- Goodrich, Tamassia ve Goldwasser, Data Structures and Algorithms in Java, 6. baskı, 2014.
- Cormen, Leiserson, Rivest ve Stein, Introduction to Algorithms, 4. baskı, 2022.
- Sedgewick ve Wayne, Algorithms, 4. baskı, 2011.