Bölüm 10: Arama Ağaçları

Veri Yapıları
İkili arama ağacıArama ve eklemeSilmeO(h) analizi Denge ve döndürmeAVL ağaçlarıKırmızı-siyah ağaçlarSplay ağaçları
Sercan KÜLCÜ · Giresun Üniversitesi · Bilgisayar Mühendisliği

Giriş

Öğrenme Çıktıları

BST işlemleri

Arama, ekleme ve silme işlemlerini ikili arama ağacında uygulamak.

Denge gereksinimi

Ağaç yüksekliğinin işlem maliyetlerine etkisini açıklamak.

Dengeleme

AVL dönüşlerini ve kırmızı-siyah ağaç kurallarını yorumlamak.

Karşılaştırma

BST, AVL, kırmızı-siyah ve splay ağaçlarını kullanım amacına göre seçmek.

Arama Ağaçları

Arama Ağacı (Search Tree)

  • Her düğüm bir öğeyi temsil eder; her öğenin bir anahtarı (key) vardır.
  • Öğeler anahtarlarına göre sıralı bir düzende saklanır.
  • Arama, kökten başlayıp her düğümde tek bir dala inerek yapılır.
  • Maliyet, ağacın yüksekliği h ile orantılıdır: O(h).
  • n düğümlü ağaçta h, en iyi ≈ log2 n, en kötü n − 1 olur.
6 2 1 4 3 8derinlik 0derinlik 1derinlik 2derinlik 3
Arama ağaçlarının bütün hikâyesi: işlemler O(h) sürer; o hâlde h'yi küçük tutmak (dengeli ağaç) işlemleri O(log n)'e indirir.

Arama Ağaçları

Temel İşlemler

Arama (Search)Verilen anahtara sahip öğeyi bulur ve döndürür; yoksa null.ara(kok, 5) → düğüm 5
Ekleme (Insertion)Yeni öğeyi sıralama düzenini bozmadan uygun yaprak konumuna ekler.ekle(kok, 7)
Silme (Deletion)Verilen anahtara sahip öğeyi çıkarır ve düzeni korur.sil(kok, 40)
Gezinme (Traversal)Öğeleri belirli bir sırayla dolaşır; inorder gezinme anahtarları sıralı verir.inorder → 1 2 3 4 6 8

Ek olarak: en küçük / en büyük anahtar, bir anahtarın ardılı / öncülü, aralık sorguları. Hepsi O(h).

Arama Ağaçları

Arama Ağacı Türleri

İkili Arama Ağacı (BST)Her düğümün anahtarı, sol alt ağaçtaki anahtarlardan büyük, sağ alt ağaçtakilerden küçüktür. Denge garantisi yoktur.
AVL AğacıHer düğümde iki alt ağacın yükseklik farkı (denge faktörü) en fazla 1'dir. Döndürmelerle dengelenir.
Kırmızı-Siyah Ağaç (Red-Black)Düğüm renkleri ve kurallarıyla yüksekliği en fazla 2·log2(n+1) ile sınırlar.
Splay AğacıErişilen düğümü döndürmelerle köke taşır; işlem başına amortize O(log n).

B-ağaçları ve B+ ağaçları (çok yollu arama ağaçları) Bölüm 7'de ele alındı.

Arama Ağaçları

İkili Arama Ağacı (Binary Search Tree)

Her düğüm x için: sol alt ağaçtaki tüm anahtarlar < x.anahtar < sağ alt ağaçtaki tüm anahtarlar.

6 2 1 4 3 8 6 2 1 4 3 7 8✗
  • Özellik yalnızca çocuklar için değil, bütün alt ağaç için geçerlidir: sağdaki ağaçta 7 > 4 doğru, ama 7, 6'nın solunda olamaz.
  • Tekrarlayan anahtarlar ya reddedilir ya da tutarlı biçimde tek tarafa (ör. sağa) konur.

Arama Ağaçları

En Küçük, En Büyük ve Sıralı Gezinme

6 2 1 4 3 8min: hep solamax: hep sağa
Inorder (sol → kök → sağ) gezinme anahtarları sıralı verir:
1 2 3 4 6 8
// Ön koşul: dugum != null
IkiliAgacDugum<E> minDeger(
        IkiliAgacDugum<E> dugum) {
    while (dugum.solCocuk != null)
        dugum = dugum.solCocuk; // sola in
    return dugum;  // en küçük
}
// maxDeger: aynısı, sagCocuk ile

void inorder(IkiliAgacDugum<E> d) {
    if (d == null) return;
    inorder(d.solCocuk);  // sol
    System.out.print(d.veri + " ");
    inorder(d.sagCocuk);  // sağ
}

Arama Ağaçları

Ağaç Düğümü Yapısı

solverisagsol çocuksağ çocuk
int anahtarlı
class AgacDugumu {
    int veri;
    AgacDugumu sol, sag;

    AgacDugumu(int veri) {
        this.veri = veri;
        this.sol = null;
        this.sag = null;
    }
}
generic
class IkiliAgacDugum<E extends Comparable<E>> {
    E veri;                    // nesne
    IkiliAgacDugum<E> solCocuk; // sol çocuk
    IkiliAgacDugum<E> sagCocuk; // sağ çocuk

    IkiliAgacDugum(E veri) {
        this.veri = veri;
        // çocuklar başlangıçta null
    }
}

Anahtarlar compareTo ile karşılaştırılır; bu yüzden E, Comparable<E> olmalıdır.

BST İşlemleri

Öğe Arama

  • Kökten başla; anahtar eşitse bulundu.
  • Küçükse sol, büyükse sağ alt ağaçta devam et.
  • null'a ulaşırsan anahtar ağaçta yoktur.
6 4 2 5 8 7 95 < 65 > 4
boolean ara(IkiliAgacDugum<E> kok, E veri) {
    if (kok == null)
        return false;            // yok
    int k = veri.compareTo(kok.veri);
    if (k == 0)
        return true;             // bulundu
    else if (k < 0)
        return ara(kok.solCocuk, veri);
    else
        return ara(kok.sagCocuk, veri);
}
Nesneleri == ile değil compareTo (veya equals) ile karşılaştır: == yalnızca referansların aynı olup olmadığına bakar.

BST İşlemleri

Arama Adım Adım: 5 ve 10

6 4 2 5 8 7 9çağrı yığını çağrı satır kok anahtar ara 7 6 5 1 AgacDugumu ara(AgacDugumu kok, int anahtar) 2 { 3 if (kok == null || kok.veri == anahtar) { 4 return kok; 5 } 6 if (anahtar < kok.veri) 7 return ara(kok.sol, anahtar); 8 else 9 return ara(kok.sag, anahtar); 10 }halka: etkin çağrının kok düğümüturuncu satır: çalışan kod satırı

ara(kök, 5): 6 ≠ 5 ve 5 < 6 → satır 7: sol alt ağaçta aramaya devam.

6 4 2 5 8 7 9çağrı yığını çağrı satır kok anahtar ara 7 6 5 ara 9 4 5 1 AgacDugumu ara(AgacDugumu kok, int anahtar) 2 { 3 if (kok == null || kok.veri == anahtar) { 4 return kok; 5 } 6 if (anahtar < kok.veri) 7 return ara(kok.sol, anahtar); 8 else 9 return ara(kok.sag, anahtar); 10 }halka: etkin çağrının kok düğümüturuncu satır: çalışan kod satırı

Yeni çağrı ara(4, 5): 5 > 4 → satır 9: sağ alt ağaç.

6 4 2 5 8 7 9çağrı yığını çağrı satır kok anahtar ara 7 6 5 ara 9 4 5 ara 4 5 5 1 AgacDugumu ara(AgacDugumu kok, int anahtar) 2 { 3 if (kok == null || kok.veri == anahtar) { 4 return kok; 5 } 6 if (anahtar < kok.veri) 7 return ara(kok.sol, anahtar); 8 else 9 return ara(kok.sag, anahtar); 10 }halka: etkin çağrının kok düğümüturuncu satır: çalışan kod satırı

ara(5, 5): kok.veri == anahtar → satır 4: düğüm 5 döndürülür.

6 4 2 5 8 7 9çağrı yığını çağrı satır kok anahtar sonuç: düğüm 5 (bulundu) 1 AgacDugumu ara(AgacDugumu kok, int anahtar) 2 { 3 if (kok == null || kok.veri == anahtar) { 4 return kok; 5 } 6 if (anahtar < kok.veri) 7 return ara(kok.sol, anahtar); 8 else 9 return ara(kok.sag, anahtar); 10 }halka: etkin çağrının kok düğümüturuncu satır: çalışan kod satırı

Her çağrı sonucu return ile yukarı iletir. 3 düğüm ziyaret edildi = derinlik + 1.

6 4 2 5 8 7 9çağrı yığını çağrı satır kok anahtar ara 9 6 10 1 AgacDugumu ara(AgacDugumu kok, int anahtar) 2 { 3 if (kok == null || kok.veri == anahtar) { 4 return kok; 5 } 6 if (anahtar < kok.veri) 7 return ara(kok.sol, anahtar); 8 else 9 return ara(kok.sag, anahtar); 10 }halka: etkin çağrının kok düğümüturuncu satır: çalışan kod satırı

Şimdi ara(kök, 10): 10 > 6 → satır 9: sağ alt ağaç.

6 4 2 5 8 7 9çağrı yığını çağrı satır kok anahtar ara 9 6 10 ara 9 8 10 1 AgacDugumu ara(AgacDugumu kok, int anahtar) 2 { 3 if (kok == null || kok.veri == anahtar) { 4 return kok; 5 } 6 if (anahtar < kok.veri) 7 return ara(kok.sol, anahtar); 8 else 9 return ara(kok.sag, anahtar); 10 }halka: etkin çağrının kok düğümüturuncu satır: çalışan kod satırı

ara(8, 10): 10 > 8 → yine sağ.

6 4 2 5 8 7 9çağrı yığını çağrı satır kok anahtar ara 9 6 10 ara 9 8 10 ara 9 9 10 1 AgacDugumu ara(AgacDugumu kok, int anahtar) 2 { 3 if (kok == null || kok.veri == anahtar) { 4 return kok; 5 } 6 if (anahtar < kok.veri) 7 return ara(kok.sol, anahtar); 8 else 9 return ara(kok.sag, anahtar); 10 }halka: etkin çağrının kok düğümüturuncu satır: çalışan kod satırı

ara(9, 10): 10 > 9 → sağ; ama 9'un sağ çocuğu yok (null).

null 6 4 2 5 8 7 9çağrı yığını çağrı satır kok anahtar ara 9 6 10 ara 9 8 10 ara 9 9 10 ara 4 null 10 1 AgacDugumu ara(AgacDugumu kok, int anahtar) 2 { 3 if (kok == null || kok.veri == anahtar) { 4 return kok; 5 } 6 if (anahtar < kok.veri) 7 return ara(kok.sol, anahtar); 8 else 9 return ara(kok.sag, anahtar); 10 }halka: etkin çağrının kok düğümüturuncu satır: çalışan kod satırı

ara(null, 10): kok == null → satır 4: null döndürülür.

6 4 2 5 8 7 9çağrı yığını çağrı satır kok anahtar sonuç: null (bulunamadı) 1 AgacDugumu ara(AgacDugumu kok, int anahtar) 2 { 3 if (kok == null || kok.veri == anahtar) { 4 return kok; 5 } 6 if (anahtar < kok.veri) 7 return ara(kok.sol, anahtar); 8 else 9 return ara(kok.sag, anahtar); 10 }halka: etkin çağrının kok düğümüturuncu satır: çalışan kod satırı

null tüm çağrılardan geri iletilir: 10 ağaçta yok. En fazla h + 1 düğüm ziyaret edilir → O(h).

BST İşlemleri

Öğe Ekleme

  • Aranıyormuş gibi in; düştüğün null konuma yeni yaprak olarak ekle.
  • Yeni düğüm her zaman yaprak olur; O(h).
6 2 1 4 3 5 8ekle 5
IkiliAgacDugum<E> ekle(
        IkiliAgacDugum<E> kok, E veri) {
    if (kok == null)
        return new IkiliAgacDugum<>(veri);
    int k = veri.compareTo(kok.veri);
    if (k < 0)
        kok.solCocuk = ekle(kok.solCocuk, veri);
    else if (k > 0)
        kok.sagCocuk = ekle(kok.sagCocuk, veri);
    // k == 0: anahtar zaten var, eklenmez
    return kok;
}
// kullanım: kok = ekle(kok, 5);
Özyinelemeli çağrının sonucu çocuk bağına geri atanır; böylece yeni düğüm, null olan bağın yerine bağlanır.

BST İşlemleri

Ekleme Adım Adım: 5, 3, 7, 1

null çağrı yığını çağrı satır kok deger ekle 3 null 5 1 AgacDugumu ekle(AgacDugumu kok, int deger) 2 { 3 if (kok == null) { 4 kok = new AgacDugumu(deger); 5 return kok; 6 } 7 if (deger < kok.veri) 8 kok.sol = ekle(kok.sol, deger); 9 else 10 kok.sag = ekle(kok.sag, deger); 11 return kok; 12 }halka: etkin çağrının kok düğümü · kesikli: henüz bağlanmadı

Ağaç boş. kok = ekle(null, 5): satır 3'teki koşul doğru.

5çağrı yığını çağrı satır kok deger ekle 5 5 5 1 AgacDugumu ekle(AgacDugumu kok, int deger) 2 { 3 if (kok == null) { 4 kok = new AgacDugumu(deger); 5 return kok; 6 } 7 if (deger < kok.veri) 8 kok.sol = ekle(kok.sol, deger); 9 else 10 kok.sag = ekle(kok.sag, deger); 11 return kok; 12 }halka: etkin çağrının kok düğümü · kesikli: henüz bağlanmadı

Satır 4–5: yeni düğüm 5 oluşturulur ve döndürülür → kök artık 5.

5çağrı yığını çağrı satır kok deger ekle 8 5 3 1 AgacDugumu ekle(AgacDugumu kok, int deger) 2 { 3 if (kok == null) { 4 kok = new AgacDugumu(deger); 5 return kok; 6 } 7 if (deger < kok.veri) 8 kok.sol = ekle(kok.sol, deger); 9 else 10 kok.sag = ekle(kok.sag, deger); 11 return kok; 12 }halka: etkin çağrının kok düğümü · kesikli: henüz bağlanmadı

ekle(5, 3): 3 < 5 → satır 8: ekle(kok.sol, 3) çağrılır.

null 5çağrı yığını çağrı satır kok deger ekle 8 5 3 ekle 3 null 3 1 AgacDugumu ekle(AgacDugumu kok, int deger) 2 { 3 if (kok == null) { 4 kok = new AgacDugumu(deger); 5 return kok; 6 } 7 if (deger < kok.veri) 8 kok.sol = ekle(kok.sol, deger); 9 else 10 kok.sag = ekle(kok.sag, deger); 11 return kok; 12 }halka: etkin çağrının kok düğümü · kesikli: henüz bağlanmadı

Yeni çağrıda kok = null (5'in solu) → satır 3 koşulu doğru.

5 3çağrı yığını çağrı satır kok deger ekle 8 5 3 ekle 5 3 3 1 AgacDugumu ekle(AgacDugumu kok, int deger) 2 { 3 if (kok == null) { 4 kok = new AgacDugumu(deger); 5 return kok; 6 } 7 if (deger < kok.veri) 8 kok.sol = ekle(kok.sol, deger); 9 else 10 kok.sag = ekle(kok.sag, deger); 11 return kok; 12 }halka: etkin çağrının kok düğümü · kesikli: henüz bağlanmadı

Satır 4–5: yeni düğüm 3 oluşturulur ve çağırana döndürülür (henüz bağlı değil).

5 3çağrı yığını çağrı satır kok deger ekle 11 5 3 1 AgacDugumu ekle(AgacDugumu kok, int deger) 2 { 3 if (kok == null) { 4 kok = new AgacDugumu(deger); 5 return kok; 6 } 7 if (deger < kok.veri) 8 kok.sol = ekle(kok.sol, deger); 9 else 10 kok.sag = ekle(kok.sag, deger); 11 return kok; 12 }halka: etkin çağrının kok düğümü · kesikli: henüz bağlanmadı

Geri dönüş: satır 8'de kok.sol = 3 atanır; satır 11: kök 5 döndürülür.

null 5 3çağrı yığını çağrı satır kok deger ekle 10 5 7 ekle 3 null 7 1 AgacDugumu ekle(AgacDugumu kok, int deger) 2 { 3 if (kok == null) { 4 kok = new AgacDugumu(deger); 5 return kok; 6 } 7 if (deger < kok.veri) 8 kok.sol = ekle(kok.sol, deger); 9 else 10 kok.sag = ekle(kok.sag, deger); 11 return kok; 12 }halka: etkin çağrının kok düğümü · kesikli: henüz bağlanmadı

ekle(5, 7): 7 ≥ 5 → satır 10: sağda ekle(null, 7); kok == null.

5 3 7çağrı yığını çağrı satır kok deger ekle 11 5 7 1 AgacDugumu ekle(AgacDugumu kok, int deger) 2 { 3 if (kok == null) { 4 kok = new AgacDugumu(deger); 5 return kok; 6 } 7 if (deger < kok.veri) 8 kok.sol = ekle(kok.sol, deger); 9 else 10 kok.sag = ekle(kok.sag, deger); 11 return kok; 12 }halka: etkin çağrının kok düğümü · kesikli: henüz bağlanmadı

Yeni düğüm 7 döner, kok.sag'a bağlanır; kök 5 döndürülür.

5 3 7çağrı yığını çağrı satır kok deger ekle 8 5 1 ekle 8 3 1 1 AgacDugumu ekle(AgacDugumu kok, int deger) 2 { 3 if (kok == null) { 4 kok = new AgacDugumu(deger); 5 return kok; 6 } 7 if (deger < kok.veri) 8 kok.sol = ekle(kok.sol, deger); 9 else 10 kok.sag = ekle(kok.sag, deger); 11 return kok; 12 }halka: etkin çağrının kok düğümü · kesikli: henüz bağlanmadı

ekle(5, 1): 1 < 5 → sola; ekle(3, 1): 1 < 3 → yine sola (satır 8).

null 5 3 7çağrı yığını çağrı satır kok deger ekle 8 5 1 ekle 8 3 1 ekle 3 null 1 1 AgacDugumu ekle(AgacDugumu kok, int deger) 2 { 3 if (kok == null) { 4 kok = new AgacDugumu(deger); 5 return kok; 6 } 7 if (deger < kok.veri) 8 kok.sol = ekle(kok.sol, deger); 9 else 10 kok.sag = ekle(kok.sag, deger); 11 return kok; 12 }halka: etkin çağrının kok düğümü · kesikli: henüz bağlanmadı

Üçüncü çağrı: kok == null (3'ün solu) → yeni düğüm 1 oluşturulacak.

5 3 1 7çağrı yığını çağrı satır kok deger sonuç: kok = 5 1 AgacDugumu ekle(AgacDugumu kok, int deger) 2 { 3 if (kok == null) { 4 kok = new AgacDugumu(deger); 5 return kok; 6 } 7 if (deger < kok.veri) 8 kok.sol = ekle(kok.sol, deger); 9 else 10 kok.sag = ekle(kok.sag, deger); 11 return kok; 12 }halka: etkin çağrının kok düğümü · kesikli: henüz bağlanmadı

Geri dönüşte 3.sol = 1, sonra 5.sol = 3 atanır (değişmeyen bağ yeniden yazılır); kök 5 döner.

BST İşlemleri

Öğe Silme: Üç Durum

Önce silinecek düğüm aranır (O(h)). Sonra çocuk sayısına göre:

1. YaprakÇocuğu yok. Ebeveynin ilgili bağı null yapılır.sil 70
2. Tek çocukDüğüm atlanır: ebeveyn doğrudan tek çocuğa bağlanır.sil 45 · sil 60
3. İki çocukDeğer, inorder ardıl (sağın en küçüğü) ya da öncül (solun en büyüğü) ile değiştirilir.sil 40
50 40 30 45 42 60 70

Örnek ağaç (sonraki slaytlarda her silme bu ağaçtan başlar)

BST İşlemleri

Silme: Yaprak ve Tek Çocuk

50 40 30 45 42 60 70

Yaprak: 70'in çocuğu yok. Ebeveyni 60'ın sağ bağı null yapılır.

50 40 30 45 42 60

70 silindi; ağacın geri kalanı değişmedi.

50 40 30 45 42 60 70

Tek çocuk (sol): 45'in yalnızca sol çocuğu 42 var. (Orijinal ağaçtan başlıyoruz.)

50 40 30 42 60 70

42, 45'in yerine 40'ın sağ çocuğu olur: ebeveyn doğrudan toruna bağlanır.

50 40 30 45 42 60 70

Tek çocuk (sağ): 60'ın yalnızca sağ çocuğu 70 var.

50 40 30 45 42 70

70, 60'ın yerine 50'nin sağ çocuğu olur. Sıralama düzeni korunur.

BST İşlemleri

Silme: İki Çocuklu Düğüm

50 40 30 45 42 60 70

İki çocuk: 40'ın hem solu (30) hem sağı (45) var; doğrudan çıkarılamaz.

50 40 30 45 42 60 70

İnorder ardıl = sağ alt ağacın en küçüğü: 45'e geç, sonra hep sola → 42.

50 42 30 45 42 60 70

Ardılın değeri (42), 40'ın düğümüne kopyalanır. Şimdi eski 42 fazladan duruyor.

50 42 30 45 60 70

Sağ alt ağaçtan eski 42 silinir; ardılın sol çocuğu olmadığı için bu hep Durum 1 veya 2'dir.

50 30 45 42 60 70

Alternatif: inorder öncül (sol alt ağacın en büyüğü = 30) kullanılabilir. İkisi de geçerli BST verir.

BST İşlemleri

Silme Kodu

IkiliAgacDugum<E> sil(IkiliAgacDugum<E> kok, E veri) {
    if (kok == null) return null;                 // anahtar yok
    int k = veri.compareTo(kok.veri);
    if (k < 0)
        kok.solCocuk = sil(kok.solCocuk, veri);        // solda ara
    else if (k > 0)
        kok.sagCocuk = sil(kok.sagCocuk, veri);        // sağda ara
    else if (kok.solCocuk != null && kok.sagCocuk != null) {  // iki çocuk
        kok.veri = minDeger(kok.sagCocuk).veri;        // ardılın değerini kopyala
        kok.sagCocuk = sil(kok.sagCocuk, kok.veri);    // ardılı sağdan sil
    }
    else if (kok.solCocuk == null)
        return kok.sagCocuk;     // yaprak (null döner) ya da yalnız sağ çocuk
    else
        return kok.solCocuk;     // yalnız sol çocuk
    return kok;
}
Dönen değer ebeveynin bağına atanır: tek çocuk ya da null dönünce düğüm ağaçtan kopar. Kullanım: kok = sil(kok, 40); (kök silinirse yeni kök bu atamayla güncellenir).

BST İşlemleri

İkili Arama Ağacı İşlemlerinin Analizi

  • Arama, ekleme ve silmenin maliyeti, erişilen en derin düğümün derinliğiyle orantılıdır: O(h).
4 2 1 3 6 5 7dengeli: h = 2 ≈ log₂ n 1 2 3 4 5devrik (zincir): h = n − 1
DurumYükseklik hArama / Ekleme / Silmen = 106 için adım
Dengeli (en iyi)≈ log2 nO(log n)≈ 20
Rastgele sıra (ortalama)≈ 1,39 log2 nO(log n)≈ 28
Devrik (en kötü, sıralı girdi)n − 1O(n)106

BST İşlemleri

Uygulama: Öğrenci Kayıtları

Anahtar olarak öğrenci numarası kullanılır; düğüm, öğrenci nesnesinin tamamını taşır.

public class Ogrenci {
    int ogrenciNo;     // anahtar
    String adSoyad;

    public Ogrenci(int ogrenciNo,
                   String adSoyad) {
        this.ogrenciNo = ogrenciNo;
        this.adSoyad = adSoyad;
    }
}
public class OgrenciDugum {
    Ogrenci ogr;           // veri
    OgrenciDugum solCocuk;
    OgrenciDugum sagCocuk;

    public OgrenciDugum(Ogrenci ogr) {
        this.ogr = ogr;
        // çocuklar başlangıçta null
    }
}
Aynı fikir Java'daki TreeMap<Integer, Ogrenci> ile hazır gelir: anahtar → değer eşlemesi, içeride dengeli bir arama ağacı (kırmızı-siyah).

BST İşlemleri

Öğrenci Ekleme ve Arama

OgrenciDugum ekle(OgrenciDugum kok, Ogrenci ogr) {
    if (kok == null) return new OgrenciDugum(ogr);
    if (ogr.ogrenciNo < kok.ogr.ogrenciNo)
        kok.solCocuk = ekle(kok.solCocuk, ogr);   // soldan devam
    else if (ogr.ogrenciNo > kok.ogr.ogrenciNo)
        kok.sagCocuk = ekle(kok.sagCocuk, ogr);   // sağdan devam
    return kok;                                   // aynı numara eklenmez
}
Ogrenci ara(OgrenciDugum kok, int numara) {
    if (kok == null) return null;                // kayıt yok
    if (numara == kok.ogr.ogrenciNo) return kok.ogr; // bulundu
    if (numara < kok.ogr.ogrenciNo)
        return ara(kok.solCocuk, numara);         // soldan devam
    return ara(kok.sagCocuk, numara);             // sağdan devam
}

ara, boolean yerine öğrenci nesnesini döndürür: bulunursa ad-soyad gibi bilgilere de erişilir. int anahtar olduğu için == ve < doğrudan kullanılabilir.

Denge ve AVL

Denge İhtiyacı

  • BST'nin şekli ekleme sırasına bağlıdır: 1, 2, 3, 4 sırayla eklenirse ağaç bir bağlı listeye dönüşür.
  • Aynı anahtarlar, farklı yapılarla daha kısa bir ağaçta tutulabilir (sağda iki seçenek).
1 2 3 4 2 1 3 4 3 1 2 4h = 3h = 2h = 2
Dengeli (self-balancing) arama ağaçları, her ekleme ve silmeden sonra yapıyı küçük yerel değişikliklerle (döndürme, renk değiştirme) düzelterek h = O(log n) garantisi verir.

Denge ve AVL

Dengeleme Örneği

Soldaki ağaçta 9 → 14 → 12 ve 76 → 54 → 72 → 67 kolları uzamış. Bu alt ağaçlar yeniden düzenlenince (turuncu yeni alt ağaç kökleri) aynı 11 anahtar daha kısa bir ağaçta durur:

50 17 9 14 12 23 19 76 54 72 67 50 17 12 9 14 23 19 72 54 67 76yükseklik 4yükseklik 3 · tüm |bf| ≤ 1
  • Yeniden düzenleme sırasında inorder sıra değişmez: 9 12 14 17 19 23 50 54 67 72 76.
  • Ağacı her seferinde baştan kurmak O(n) olurdu; AVL ve kırmızı-siyah ağaçlar bunu yalnızca eklenen/silinen yol üzerinde, O(log n) sürede yapar.

Denge ve AVL

AVL Ağacı

  • Adelson-Velsky ve Landis (1962): ilk dengeli BST.
  • Denge faktörü: bf(x) = h(sol) − h(sağ). Boş alt ağacın yüksekliği −1.
  • AVL koşulu: her düğümde bf ∈ {−1, 0, +1}.
  • Sonuç: h < 1,44 log2(n + 2) → tüm işlemler O(log n).
  • Her düğüm yüksekliğini saklar; ekleme/silmeden sonra kökte doğru yukarı çıkılırken bf kontrol edilir.
Nasıl okunur?Düğümlerin sağ üstündeki sayı bf'dir. Pozitif: sol ağır, negatif: sağ ağır. |bf| = 2 olan ilk (en alttaki) düğüm döndürülür.
300 200 100 250 40−1 500 302 202 101 50 400AVL ✓AVL değil: 20 ve 30'da |bf| = 2

Denge ve AVL

Döndürme (Rotation)

T3T1T2 y xsağa döndür(y)T1T2T3 x y inorder her iki tarafta aynı: T1 · x · T2 · y · T3

Sağa döndürme (sol ağır y): x yukarı çıkar, y x'in sağ çocuğu olur; x'in sağ alt ağacı T2, y'nin soluna geçer.

T1T2T3 x ysola döndür(x)T3T1T2 y x inorder her iki tarafta aynı: T1 · x · T2 · y · T3

Sola döndürme (sağ ağır x): simetrik. Yalnızca 3 bağ değişir (kalın çizgiler) → O(1).

Döndürme BST özelliğini korur (inorder sıra aynı kalır), ama alt ağaçların yüksekliklerini değiştirir: bir taraf bir seviye kısalır, diğeri uzar.

Denge ve AVL

Dört Dengesizlik Durumu

LL: sol çocuğun soluna ekleme 302 201 100 200 100 300 30'da bf = 2 sağa döndür(30)
RR: sağ çocuğun sağına ekleme 10−2 20−1 300 200 100 300 10'da bf = −2 sola döndür(10)

LL durumunda sağa, RR durumunda sola tek dönüş yapılır.

Denge ve AVL

Çift Dönüşler: LR ve RL

LR: sol çocuğun sağına ekleme 302 10−1 200 302 201 100 200 100 300 bf: 2, sol çocuk −1 sola döndür(10) sağa döndür(30)
RL: sağ çocuğun soluna ekleme 10−2 301 200 10−2 20−1 300 200 100 300 bf: −2, sağ çocuk 1 sağa döndür(30) sola döndür(10)

LR: önce sola, sonra sağa. RL: önce sağa, sonra sola dönüş yapılır.

Denge ve AVL

AVL Ekleme Örneği: 10, 20, 30, 40, 50, 25

ekleme sırası:102030405025100Denge kontrolü: tüm düğümlerde |bf| ≤ 1 ✓

10 kök olur. Tek düğümün bf'si 0.

ekleme sırası:102030405025 10−1 200Denge kontrolü: tüm düğümlerde |bf| ≤ 1 ✓

20 > 10 → sağa. bf(10) = −1 − 0 = −1: hâlâ AVL.

ekleme sırası:102030405025 10−2 20−1 300Dengesiz: 10 (bf = −2) ✗

30 → 10'un sağındaki 20'nin sağına. bf(10) = −2 ✗ — sağ-sağ: RR durumu.

ekleme sırası:102030405025 200 100 300Denge kontrolü: tüm düğümlerde |bf| ≤ 1 ✓

10'da sola döndürme: 20 kök olur, 10 onun sol çocuğu. Tüm bf = 0.

ekleme sırası:102030405025 20−1 100 30−1 400Denge kontrolü: tüm düğümlerde |bf| ≤ 1 ✓

40 → 20'nin sağı → 30'un sağı. bf(30) = −1, bf(20) = −1: denge bozulmadı.

ekleme sırası:102030405025 20−2 100 30−2 40−1 500Dengesiz: 20 (bf = −2), 30 (bf = −2) ✗

50 eklenince bf(30) = −2 ve bf(20) = −2. En alttaki dengesiz düğüm 30 → RR.

ekleme sırası:102030405025 20−1 100 400 300 500Denge kontrolü: tüm düğümlerde |bf| ≤ 1 ✓

30'da sola döndürme: 40 yukarı çıkar, 30 onun sol çocuğu olur. bf(20) = −1.

ekleme sırası:102030405025 20−2 100 401 301 250 500Dengesiz: 20 (bf = −2) ✗

25 → 20'nin sağı, 40'ın solu, 30'un solu. bf(20) = −2 ✗; sağ çocuk 40 sol ağır (+1) → RL durumu.

ekleme sırası:102030405025 20−2 100 30−1 250 40−1 500Dengesiz: 20 (bf = −2) ✗

RL, 1. döndürme: 40'ta sağa döndür → üçgen düz çizgiye (sağ-sağ) dönüşür. 20 hâlâ −2.

ekleme sırası:102030405025 300 200 100 250 40−1 500Denge kontrolü: tüm düğümlerde |bf| ≤ 1 ✓

RL, 2. döndürme: 20'de sola döndür → 30 kök olur; 25, 20'nin sağına geçer. Ağaç yeniden AVL.

Denge ve AVL

AVL Döndürme ve Dengeleme Kodu

int h(AvlDugum d) {
    return d == null ? -1 : d.yukseklik;
}
int bf(AvlDugum d) {
    return h(d.sol) - h(d.sag);
}
void guncelle(AvlDugum d) {
    d.yukseklik = 1 +
        Math.max(h(d.sol), h(d.sag));
}
AvlDugum sagaDondur(AvlDugum y) {
    AvlDugum x = y.sol;
    y.sol = x.sag;  // T2 → y.sol
    x.sag = y;      // y → x.sag
    guncelle(y);    // önce alttaki
    guncelle(x);
    return x;       // yeni kök
}
AvlDugum dengele(AvlDugum d) {
    guncelle(d);
    if (bf(d) > 1) {       // sol ağır
        if (bf(d.sol) < 0)  // LR
            d.sol = solaDondur(d.sol);
        return sagaDondur(d); // LL
    }
    if (bf(d) < -1) {      // sağ ağır
        if (bf(d.sag) > 0)  // RL
            d.sag = sagaDondur(d.sag);
        return solaDondur(d); // RR
    }
    return d;
}
// solaDondur: sol ↔ sag simetriği
// ekle/sil BST gibi; dönüşte:
//   return dengele(kok);

Özyineleme geri dönerken yol üzerindeki her düğümde dengele çağrılır; silmede birden fazla düğümde döndürme gerekebilir (en fazla O(log n)).

Kırmızı-Siyah Ağaçlar

Kırmızı-Siyah Ağaç (Red-Black Tree)

  1. Her düğüm kırmızı ya da siyahtır.
  2. Kök siyahtır.
  3. Tüm yapraklar (NIL, boş çocuklar) siyah kabul edilir.
  4. Kırmızı düğümün çocukları siyahtır: ardışık iki kırmızı olmaz.
  5. Bir düğümden NIL yapraklara giden her yolda aynı sayıda siyah düğüm vardır (siyah yükseklik).
13 8 1 6 11 17 15 25 22 27

Örnek: kökten her NIL'e giden yolda 2 siyah düğüm var (kök dahil, NIL hariç)

Kırmızı-Siyah Ağaçlar

Kurallar Neden Dengeyi Garanti Eder?

En kısa yolKökten bir NIL'e giden en kısa yol yalnızca siyah düğümlerden oluşabilir: b siyah düğüm.
En uzun yolKırmızılar ardışık olamaz; en fazla her siyahın arasına bir kırmızı girer: en çok 2b düğüm.
SonuçEn uzun yol, en kısa yolun en fazla 2 katıdır. Buradan h ≤ 2·log2(n + 1).
MaliyetArama, ekleme, silme O(log n). Eklemede en fazla 2, silmede en fazla 3 döndürme; gerisi renk değiştirme.
Kırmızı-siyah ağaç, AVL'ye göre daha gevşek dengelidir (biraz daha yüksek olabilir) ama güncellemeleri daha ucuzdur. Java TreeMap/TreeSet ve C++ std::map bu yapıyı kullanır.

Kırmızı-Siyah Ağaçlar

Kırmızı-Siyah Ağaca Ekleme

Yeni düğüm BST gibi eklenir ve kırmızı boyanır (siyah yükseklik bozulmasın diye). Ebeveyn de kırmızıysa kural 4 bozulur; amcaya (ebeveynin kardeşi) bakılır:

Amca kırmızıRenk değiştir: ebeveyn ve amca siyah, dede kırmızı. Sorun dedeye taşınabilir → oradan devam et.
Amca siyah, üçgenDüğüm-ebeveyn-dede zikzak yapıyorsa ebeveynde döndür → çizgi durumuna dönüşür.
Amca siyah, çizgiDedede döndür; eski ebeveyn siyah, eski dede kırmızı olur. Bitti.
  • Ebeveyn siyahsa hiçbir şey yapılmaz. En sonda kök her zaman siyaha boyanır.
  • NIL amca siyah sayılır.
  • Silme benzer ama daha çok durumludur ("çift siyah" düzeltmesi); bu derste ayrıntısına girmiyoruz.
g p x udedeebeveynamcayeni düğüm

Kırmızı-Siyah Ağaçlar

Kırmızı-Siyah Ekleme: 10, 20, 30, 15

10Kural kontrolü: kök siyah ✓ · ardışık kırmızı yok ✓ · her NIL yolunda 1 siyah ✓

10 eklenir (kırmızı); kök olduğu için siyaha boyanır.

10 20Kural kontrolü: kök siyah ✓ · ardışık kırmızı yok ✓ · her NIL yolunda 1 siyah ✓

20 → 10'un sağına, kırmızı. Ebeveyn siyah: kural bozulmadı.

10 20 30Kural kontrolü: kök siyah ✓ · ardışık kırmızı yok ✗ · her NIL yolunda 1 siyah ✓

30 → 20'nin sağına, kırmızı: 20–30 ardışık kırmızı. Amca (10'un solu) NIL = siyah; 10–20–30 düz çizgi.

20 10 30Kural kontrolü: kök siyah ✓ · ardışık kırmızı yok ✓ · her NIL yolunda 1 siyah ✓

Çizgi durumu: dede 10'da sola döndür, renkleri değiştir: 20 siyah, 10 kırmızı.

20 10 15 30Kural kontrolü: kök siyah ✓ · ardışık kırmızı yok ✗ · her NIL yolunda 1 siyah ✓

15 → 20'nin solu, 10'un sağı; kırmızı. Ebeveyn 10 kırmızı ✗, amca 30 kırmızı.

20 10 15 30Kural kontrolü: kök siyah ✗ · ardışık kırmızı yok ✓ · her NIL yolunda 1 siyah ✓

Renk değiştir: ebeveyn 10 ve amca 30 siyah, dede 20 kırmızı. Ama 20 kök ✗.

20 10 15 30Kural kontrolü: kök siyah ✓ · ardışık kırmızı yok ✓ · her NIL yolunda 2 siyah ✓

Kök yeniden siyaha boyanır; siyah yükseklik bir artar. Tüm kurallar sağlanır.

Kırmızı-Siyah Ağaçlar

Kırmızı-Siyah Ekleme: 12 ve 5

20 10 15 30Kural kontrolü: kök siyah ✓ · ardışık kırmızı yok ✓ · her NIL yolunda 2 siyah ✓

Önceki ağaçtan devam: sırada 12 ve 5 var.

20 10 15 12 30Kural kontrolü: kök siyah ✓ · ardışık kırmızı yok ✗ · her NIL yolunda 2 siyah ✓

12 → 20'nin solu, 10'un sağı, 15'in solu. 15–12 ardışık kırmızı; amca (10'un solu) NIL = siyah; 10–15–12 üçgen.

20 10 12 15 30Kural kontrolü: kök siyah ✓ · ardışık kırmızı yok ✗ · her NIL yolunda 2 siyah ✓

Üçgen → çizgi: ebeveyn 15'te sağa döndür. Şimdi 10–12–15 düz çizgi (hâlâ ardışık kırmızı).

20 12 10 15 30Kural kontrolü: kök siyah ✓ · ardışık kırmızı yok ✓ · her NIL yolunda 2 siyah ✓

Çizgi: dede 10'da sola döndür; 12 siyah, 10 kırmızı olur. Kurallar sağlandı.

20 12 10 5 15 30Kural kontrolü: kök siyah ✓ · ardışık kırmızı yok ✗ · her NIL yolunda 2 siyah ✓

5 → 20 → 12 → 10'un solu, kırmızı. Ebeveyn 10 kırmızı ✗; amca 15 kırmızı.

20 12 10 5 15 30Kural kontrolü: kök siyah ✓ · ardışık kırmızı yok ✓ · her NIL yolunda 2 siyah ✓

Renk değiştir: 10 ve 15 siyah, dede 12 kırmızı. 12'nin ebeveyni 20 siyah → bitti, döndürme gerekmedi.

Kırmızı-Siyah Ağaçlar

AVL mi, Kırmızı-Siyah mı?

ÖzellikAVLKırmızı-Siyah
Denge ölçütü|bf| ≤ 1 her düğümderenk kuralları, eşit siyah yükseklik
En büyük yükseklik≈ 1,44 log2 n2 log2(n + 1)
AramaO(log n), biraz daha hızlı (daha kısa)O(log n)
Ekleme döndürmesien fazla 1 (tek veya çift)en fazla 2
Silme döndürmesiO(log n) olabiliren fazla 3
Düğümde ek bilgiyükseklik (veya bf)1 bit renk
Terciharamanın baskın olduğu işlersık ekleme/silme, kütüphaneler

Splay Ağaçları

Splay Ağacı (Splay Tree)

  • Sleator ve Tarjan (1985): kendini düzenleyen (self-adjusting) BST.
  • Her erişimde (arama, ekleme, silme) ilgili düğüm döndürmelerle köke taşınır: splay işlemi.
  • Düğümlerde denge bilgisi (yükseklik, renk) tutulmaz.
  • Tek bir işlem O(n) sürebilir; ama m işlemlik her dizi O(m log n) sürer: amortize O(log n).
YerellikSık erişilen anahtarlar köke yakın durur; önbellek gibi davranır.
BasitlikEk alan yok, tek bir splay yordamı tüm işlemlere yeter.
DezavantajArama bile ağacı değiştirir; tek işlem için garanti yok.

Splay Ağaçları

Splay Adımları: Zig, Zig-Zig, Zig-Zag

x: splay edilen düğüm, p: ebeveyni, g: dedesi. x kök olana kadar bu adımlar tekrarlanır.

20 10 5 15 30 x = 10, ebeveyni p = 20 kök sağa döndür(p) 10 5 20 15 30 x kök oldu; 15, p'nin soluna geçti

Zig: x'in ebeveyni p köktür → p'de tek döndürme. (Yalnızca son adımda olabilir.)

30 20 10 x, p, g aynı yönde (sol-sol) döndür(g) 20 10 30 önce dede g = 30 döndür(p) 10 20 30 sonra ebeveyn p = 20

Zig-zig: x ve p aynı yönde çocuk → önce g'de, sonra p'de döndür. (AVL'deki tek döndürmeden farklı!)

30 10 20 x sağ, p sol çocuk (zikzak) sola döndür(p) 30 20 10 önce ebeveyn p = 10 sağa döndür(g) 20 10 30 sonra dede g = 30

Zig-zag: x ve p zıt yönde → önce p'de, sonra g'de döndür (AVL çift döndürmesiyle aynı).

Splay Ağaçları

Splay Örneği: splay(50), sonra splay(10)

80 40 20 10 30 60 50 70 90yükseklik 3

splay(50): önce normal BST araması: 80 → 40 → 60 → 50.

80 40 20 10 30 50 60 70 90yükseklik 4

x = 50 sol, p = 60 sağ çocuk → zig-zag. Önce p = 60'ta sağa döndür.

80 50 40 20 10 30 60 70 90yükseklik 4

Sonra g = 40'ta sola döndür: 50, 40 ile 60'ın ebeveyni olur.

50 40 20 10 30 80 60 70 90yükseklik 3

50'nin ebeveyni 80 kök → zig: 80'de sağa döndür. 50 artık kök; 60, 80'in soluna geçti.

50 40 20 10 30 80 60 70 90yükseklik 3

Şimdi splay(10): yol 50 → 40 → 20 → 10. x = 10, p = 20, g = 40 hepsi sol → zig-zig.

50 20 10 40 30 80 60 70 90yükseklik 3

Zig-zig, önce dede: g = 40'ta sağa döndür (20 yukarı, 30 → 40'ın soluna).

50 10 20 40 30 80 60 70 90yükseklik 4

Sonra ebeveyn: p = 20'de sağa döndür. 10 yukarı çıktı.

10 50 20 40 30 80 60 70 90yükseklik 4

Zig: kök 50'de sağa döndür → 10 kök. Yakın zamanda erişilen 50 hâlâ köke yakın (derinlik 1).

Özet

Özet: Arama Ağaçlarında İşlem Maliyetleri

YapıYükseklikAramaEklemeSilmeNasıl dengelenir?
BSTlog n … nO(h)O(h)O(h)dengelenmez; en kötü O(n)
AVL≤ 1,44 log nO(log n)O(log n)O(log n)bf ∈ {−1, 0, 1}; LL, RR, LR, RL döndürmeleri
Kırmızı-Siyah≤ 2 log(n+1)O(log n)O(log n)O(log n)renk değiştirme + en çok 2–3 döndürme
SplaysınırsızO(log n)*O(log n)*O(log n)*erişilen düğüm zig / zig-zig / zig-zag ile köke

* Amortize; tek işlem O(n) olabilir. Tüm yapılarda inorder gezinme O(n) ve anahtarları sıralı verir; min/max O(h).

BST özelliğisol < kök < sağ, tüm alt ağaç için.
Silmeyaprak → kaldır; tek çocuk → atla; iki çocuk → ardıl/öncül.
DöndürmeO(1), inorder sırayı korur, yükseklikleri değiştirir.

Özet

Etkileşimli Simülatörler

Kendi anahtarlarınızla deneyin: ekleyin, silin, döndürmeleri adım adım izleyin.

İkili Arama AğacıArama, ekleme, üç silme durumu ve gezinmeler.
AVL AğacıDenge faktörleri ve LL / RR / LR / RL döndürmeleri.
Kırmızı-Siyah AğaçRenk değiştirme ve döndürmelerle ekleme.
Splay AğacıErişilen düğümün zig, zig-zig, zig-zag ile köke taşınması.

Özet

Örnek Problemler

  1. Boş BST'ye 50, 30, 70, 20, 40, 60, 80 sırayla eklenir. Yükseklik nedir? Inorder çıktı nedir?
  2. Aynı anahtarlar 20, 30, 40, 50, 60, 70, 80 sırasıyla eklenirse yükseklik ve arama maliyeti ne olur?
  3. 1. sorudaki ağaçtan kök 50, inorder ardıl kullanılarak silinirse ağaç nasıl olur?
  4. Boş AVL ağacına 3, 2, 1 eklenir. Hangi durum oluşur, hangi döndürme yapılır?
  5. Boş AVL ağacına 10, 30, 20 eklenir. Hangi durum oluşur, sonuç ağaç nedir?
  6. Kırmızı-siyah ağaçta kökten NIL'e giden en uzun yol, en kısa yolun en fazla kaç katıdır? Neden?
  7. Splay ağacında zig-zig adımında döndürmeler hangi sırayla yapılır?
Cevapları göster
  1. h = 2 (tam dengeli); inorder: 20 30 40 50 60 70 80.
  2. Sağa doğru zincir: h = 6; arama O(n) (en kötü 7 karşılaştırma).
  3. Ardıl 60 köke kopyalanır, 70'in solundaki 60 silinir: 60(30(20, 40), 70(—, 80)).
  4. 3'te bf = 2, sol çocuk 2'de bf = 1 → LL; 3'te sağa döndürme → 2(1, 3).
  5. 10'da bf = −2, sağ çocuk 30'da bf = +1 → RL; 30'da sağa, 10'da sola → 20(10, 30).
  6. 2 katı: siyah sayısı her yolda eşit ve kırmızılar ardışık olamaz.
  7. Önce dede (g), sonra ebeveyn (p) döndürülür.

Ö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.

Son

Bölüm 10: Arama Ağaçları
Sonraki bölüm: Çizgeler
1 / 1 Sercan KÜLCÜ, Tüm hakları saklıdır.

← → veya boşluk ile gezinin · F: tam ekran · Home/End: ilk/son slayt