SEN2212 · 5. hafta · Introduction to balanced trees and implementing AVL balanced tree structure using Java.

AVL ağaçları: denge faktörü ve rotasyonlar

BST'yi O(log n)'de tutan AVL kuralı, LL/RR/LR/RL rotasyonları ve sınavın klasik sorusu: bu sırayla eklenince ağaç neye benzer?

AVL treebalance factorheightsingle rotationdouble rotationLL caseRR caseLR caseRL caseself-balancing BST

Geçen hafta gördüğün ikili arama ağacı (BST) güzel ama kırılgan. Anahtarları sıralı eklersen (1, 2, 3, …) ağaç sağa doğru bir zincire döner ve arama O(n) olur. AVL ağacı (Adelson-Velsky ve Landis) bu sorunu çözen ilk kendini dengeleyen (self-balancing) BST’dir. Sınavda neredeyse her zaman aynı soru gelir: “Şu anahtarları boş bir AVL ağacına sırayla ekle, son ağacı çiz.” Bu sayfadaki dört rotasyonu ezberden değil, mantığıyla bilen biri bu soruyu kaçırmaz.

Altın kurallar:

  1. AVL kuralı: her düğümde sol ve sağ alt ağacın yükseklik farkı en fazla 1.
  2. Yükseklik: boş ağaç -1, yaprak 0. Denge faktörü (balance factor) bf = height(sol) - height(sağ).
  3. Ekledikten sonra yukarı çık. bf değeri +2 ya da -2 olan ilk (en derindeki) düğümde dur ve düzelt. Eklemede tek düzeltme yeter.
  4. Yön sorusu: yeni anahtar o düğümden itibaren hangi iki adımı attı? Sol-sol, sağ-sağ, sol-sağ, sağ-sol. Dört durumun adı buradan gelir.

Yükseklik ve denge faktörü

Bir düğümün yüksekliği (height), ondan en uzak yaprağa giden yoldaki kenar sayısıdır. Hesabı her zaman alttan yukarı yap:

height(t)=1+max⁡(height(t.left), height(t.right)),height(null)=−1height(t) = 1 + \max(height(t.left),\ height(t.right)), \qquad height(null) = -1

        30  h=2, bf=1-0=1
       /  \
     20    40   h(20)=1 bf=1, h(40)=0
    /
  10            h=0, bf=0

Bu ağaç AVL: bütün bf değerleri -1, 0 ya da 1. 10’un altına 5 eklersen 20’nin bf’si 2 olur ve kural bozulur.

TUZAK · Yükseklik ile derinlik karışmasın. Derinlik (depth) kökten aşağı sayılır, yükseklik yapraktan yukarı. bf hesabında hep yükseklik kullanılır. Bazı kaynaklar yaprağa 1 der; o zaman tüm değerler 1 kayar ama bf aynı çıkar. Sınavda hocanın tanımına bak.

Neden O(log n)?

En az düğümle h yüksekliğinde AVL ağacı kurmak istersen, bir alt ağacı h-1, diğerini h-2 yüksekliğinde yaparsın (fark 1’e izin var). Buradan:

N(h)=N(h−1)+N(h−2)+1,N(0)=1, N(1)=2N(h) = N(h-1) + N(h-2) + 1, \qquad N(0)=1,\ N(1)=2

h01234567
N(h) en az düğüm124712203354

Bu dizi Fibonacci gibi üstel büyür. Düğüm sayısı yükseklikle üstel artıyorsa, yükseklik düğüm sayısıyla logaritmik artar: h<1.44log⁡2nh < 1.44 \log_2 n. Arama, ekleme ve silme yükseklik kadar adım attığı için hepsi O(log n). Rotasyon sadece birkaç işaretçi değiştirir, O(1).

Dört durum ve rotasyonlar

Ekleme normal BST eklemesi gibi yapılır. Sonra eklenen yaprağın atalarına yukarı doğru bakılır. İlk dengesiz düğüme k diyelim. Yeni anahtarın k’dan sonra attığı iki adım durumu belirler.

LL ve RR: tek rotasyon

LL durumunda yeni anahtar k’nın sol çocuğunun sol alt ağacına gitmiştir. Sol çocuk (k1) yukarı çıkar, k2 sağa iner. X, Y, Z alt ağaçlardır:

LL: k2'de saga rotasyon
        k2                k1
       /  \              /  \
     k1    Z    ==>     X    k2
    /  \                    /  \
   X    Y                  Y    Z

Dikkat: k1’in sağındaki Y alt ağacı k2’nin soluna geçer. Y’deki her anahtar k1’den büyük, k2’den küçüktür. Yani yeni yerinde de BST sırası korunur. Inorder sıra X, k1, Y, k2, Z her iki çizimde aynıdır.

RR bunun ayna görüntüsüdür: k1 üzerinde sola rotasyon, sağ çocuk k2 köke çıkar, k2’nin sol alt ağacı k1’in sağına geçer.

RR: 10'da sola rotasyon (10, 20, 30 eklendi)
  10                  20
    \                /  \
     20     ==>    10    30
       \
        30

LR ve RL: çift rotasyon

LR durumunda yeni anahtar k’nın solunun sağına gitmiştir. Tek sağa rotasyon burada işe yaramaz, sadece eğriliği diğer tarafa taşır. Çözüm iki adım: önce sol çocukta sola rotasyon (durumu LL’ye çevirir), sonra k’da sağa rotasyon. Sonuçta ortadaki düğüm (k1 ile k3 arasındaki) en üste çıkar.

LR: 30, 10, 20 eklendi
    30          30           20
   /           /            /  \
  10    ==>   20    ==>   10    30
    \        /
     20     10
  10'da sola   30'da saga

RL ayna görüntüsü: önce sağ çocukta sağa, sonra k’da sola rotasyon.

İPUCU · Çift rotasyonun kısa yolu. k, çocuğu ve torunu (yeni anahtarın yolundaki üç düğüm) sıraya diz. Ortanca değer yeni kök olur, küçüğü sola, büyüğü sağa gider. Torunun eski iki alt ağacı sırayla sol ve sağ tarafa dağıtılır.

DurumYeni anahtarın yolubf(k)Düzeltme
LLsol, sol+2k’da tek sağa rotasyon
RRsağ, sağ-2k’da tek sola rotasyon
LRsol, sağ+2sol çocukta sola, sonra k’da sağa
RLsağ, sol-2sağ çocukta sağa, sonra k’da sola

Java’da AVL ekleme

Düğüm kendi yüksekliğini tutar. Ekleme özyinelemelidir: geri dönerken her düğüm yüksekliğini günceller ve gerekirse dengelenir. balance metodu yükseklikleri karşılaştırdığı için hem ekleme hem silme için çalışır.

static class AvlNode {
    int key, height;          // yaprak: height = 0
    AvlNode left, right;
    AvlNode(int key) { this.key = key; }
}

static int height(AvlNode t) { return t == null ? -1 : t.height; }

static void update(AvlNode t) {
    t.height = 1 + Math.max(height(t.left), height(t.right));
}

static AvlNode rotateRight(AvlNode k2) {   // LL
    AvlNode k1 = k2.left;
    k2.left = k1.right;
    k1.right = k2;
    update(k2);
    update(k1);
    return k1;
}

static AvlNode rotateLeft(AvlNode k1) {    // RR
    AvlNode k2 = k1.right;
    k1.right = k2.left;
    k2.left = k1;
    update(k1);
    update(k2);
    return k2;
}

static AvlNode insert(AvlNode t, int x) {
    if (t == null) return new AvlNode(x);
    if (x < t.key) t.left = insert(t.left, x);
    else if (x > t.key) t.right = insert(t.right, x);
    else return t;                         // kopya eklenmez
    return balance(t);
}

static AvlNode balance(AvlNode t) {
    update(t);
    int bf = height(t.left) - height(t.right);
    if (bf > 1) {
        if (height(t.left.left) < height(t.left.right))
            t.left = rotateLeft(t.left);   // LR: once LL'ye cevir
        return rotateRight(t);
    }
    if (bf < -1) {
        if (height(t.right.right) < height(t.right.left))
            t.right = rotateRight(t.right); // RL
        return rotateLeft(t);
    }
    return t;
}

Kullanım hep root = insert(root, x); biçimindedir. Rotasyon alt ağacın kökünü değiştirdiği için dönen değeri atamak şart.

Çözümlü örnek: 15, 20, 24, 10, 13, 7, 30, 36, 25

Boş bir AVL ağacına bu dokuz anahtarı ekleyelim. Her adımda yolu takip et, yukarı doğru bf kontrol et.

+15, +20: sorun yok. +24: 15’in bf’si -2, yol sağ-sağ: RR, 15’te sola rotasyon.

  15                 20
    \               /  \
     20    ==>    15    24
       \
        24

+10: 15’in soluna, dengeli. +13: 13 → 20 → 15 → 10 → sağ. 15’in bf’si 2 (sol yükseklik 1, sağ -1). Yol sol-sağ: LR. 10’da sola, 15’te sağa. 13 ortaya çıkar.

       20                   20
      /  \                 /  \
    15    24    ==>      13    24
   /                    /  \
  10                  10    15
    \
     13

+7: 7 → 20 → 13 → 10 → sol. Yukarı bak: 10’un bf’si 1, 13’ün bf’si 1, 20’nin bf’si height(13) - height(24) = 2 - 0 = 2. Yol (20’den) sol-sol: LL, 20’de sağa rotasyon. 13 köke çıkar, 13’ün sağ çocuğu 15, 20’nin soluna geçer.

         13
        /  \
      10    20
     /     /  \
    7    15    24

+30: 24’ün sağına, dengeli. +36: 24’ün bf’si -2, sağ-sağ: RR, 24’te sola rotasyon, 30 yukarı çıkar.

         13
        /  \
      10    20
     /     /  \
    7    15    30
              /  \
            24    36

+25: 25 → 13 → 20 → 30 → 24 → sağ. 24’ün bf’si -1, 30’un bf’si 1, 20’nin bf’si 0 - 2 = -2. Yol (20’den) sağ-sol: RL. Önce 30’da sağa rotasyon (24 yukarı), sonra 20’de sola rotasyon (24, 20’nin yerine geçer).

          13
        /    \
      10      24
     /       /  \
    7      20    30
          /     /  \
        15    25    36

Program çıktısı (her eklemeden sonra seviye sırası ve yapılan düzeltme):

+15 -> 15
+20 -> 15 20
+24 -> 20 15 24   [RR at 15]
+10 -> 20 15 24 10
+13 -> 20 13 24 10 15   [LR at 15]
+7 -> 13 10 20 7 15 24   [LL at 20]
+30 -> 13 10 20 7 15 24 30
+36 -> 13 10 20 7 15 30 24 36   [RR at 24]
+25 -> 13 10 24 7 20 30 15 25 36   [RL at 20]

Son ağacın yüksekliği 3. Kökün bf’si 1 - 2 = -1, kural tutuyor.

Silme kısaca

AVL’den silme önce normal BST silmesidir (iki çocuklu düğümde sağ alt ağacın en küçüğü, inorder successor, yerine geçer). Sonra silinen yerden köke kadar her düğümde balance çağrılır. Eklemeden farkı: silmede bir rotasyon alt ağacın yüksekliğini azaltabilir ve dengesizlik yukarıya taşınabilir. Yani köke kadar birden fazla rotasyon gerekebilir. Durum seçimi yine çocukların yüksekliğine bakılarak yapılır; yukarıdaki balance metodu bunu zaten yapıyor. Süre yine O(log n).

Sık yapılan hatalar

Yanlış düşünceDoğrusu
Kökten başlayıp ilk dengesiz düğümü düzeltirimYeni yapraktan yukarı çık; en derindeki dengesiz düğümü düzelt
LR’de tek sağa rotasyon yeterTek rotasyon eğriliği aynaya çevirir; çift rotasyon gerekir
Rotasyonda Y alt ağacı kaybolur ya da yerinde kalırY her zaman yukarı çıkan düğümden aşağı inen düğüme geçer
Yaprağın yüksekliği 1, boş ağaç 0Bu derste: yaprak 0, null -1
Eklemeden sonra birkaç rotasyon gerekebilirEklemede en fazla bir (tek ya da çift) düzeltme yeter; silmede birden çok olabilir
rotateRight(t); yazmak yeterliDönen yeni kökü ata: t = rotateRight(t);
Yükseklikleri herhangi bir sırada güncellerimÖnce aşağı inen düğüm, sonra yeni kök
bf’yi alt ağaçtaki düğüm sayısıyla hesaplarımbf yükseklik farkıdır, düğüm sayısı değil

Kendini test et

Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.

Soru 1kolay

In the tree below, what is the balance factor of the root (height(left) - height(right)), and is the tree an AVL tree?

        20
       /  \
     10    30
    /  \
   5    15
  /
 3
Cevabı göster
2, not AVL

Yükseklikleri alttan hesapla (boş = -1, yaprak = 0). 3: 0. 5: 1. 15: 0. 10: 1 + max(1, 0) = 2. 30: 0.
Kök: height(10) - height(30) = 2 - 0 = 2. |bf| > 1 olduğu için AVL değil. 10 ve 5 düğümlerinin bf değeri 1, onlar sorun değil.

Soru 2kolay

Insert 10, 20, 30 (in this order) into an empty AVL tree. Which key is the root, and which rotation was used?

Cevabı göster
20, single left rotation (RR case)

10 ve 20 sorunsuz. 30 eklenince 10'un bf'si -2 olur. Yeni anahtar 10'un sağının sağına gitti: RR durumu.
10 üzerinde tek sola rotasyon: 20 köke çıkar, 10 sol, 30 sağ çocuk olur.

Soru 3kolay

Keys 30, 10, 20 are inserted into an empty AVL tree in this order. Which case occurs at node 30 and what kind of rotation fixes it?

Cevabı göster
LR case, double rotation (left at 10, then right at 30)

20, 30'un solunun (10) sağına gidiyor: sol-sağ, yani LR. Tek rotasyon bunu düzeltmez.
Önce 10 üzerinde sola rotasyon (20 yukarı çıkar), sonra 30 üzerinde sağa rotasyon. Sonuç: kök 20, çocuklar 10 ve 30.

Soru 4kolay

What is the worst-case time complexity of search and insert in an AVL tree with n nodes? Compare with an ordinary BST.

Cevabı göster
AVL: O(log n); plain BST: O(n)

AVL ağacının yüksekliği her zaman yaklaşık 1.44 log2 n'den küçük kalır. Arama ve ekleme yükseklik kadar adım atar, rotasyonlar O(1). Bu yüzden O(log n).
Sıradan BST sıralı veriyle (1, 2, 3, ...) bağlı listeye döner, yükseklik n - 1 olur: O(n).

Soru 5orta

Insert 1, 2, 3, 4, 5, 6, 7 into an empty AVL tree. Give the level-order traversal of the final tree and the number of rotations performed.

Cevabı göster
4 2 6 1 3 5 7, 4 rotations

Hepsi RR durumu, hepsi tek sola rotasyon:
+3: 1'de dengesizlik, kök 2 olur.
+5: 3'te dengesizlik, 4 yukarı çıkar: 2(1, 4(3,5)).
+6: kökte (2) dengesizlik, 4 köke çıkar, 3 alt ağacı 2'nin sağına geçer: 4(2(1,3), 5(-,6)).
+7: 5'te dengesizlik, 6 yukarı: 4(2(1,3), 6(5,7)).
Sonuç tam (perfect) ağaç, yükseklik 2. Normal BST'de aynı sıra yüksekliği 6 olan bir zincir verirdi.

Soru 6orta

Insert 60, 50, 40, 30, 20, 10 into an empty AVL tree. Give the level-order traversal of the result.

Cevabı göster
30 20 50 10 40 60

Azalan sıra, hepsi LL ve tek sağa rotasyon:
+40: 60'ta LL, kök 50: 50(40,60).
+30: dengesizlik yok. +20: 40'ta LL, 30 yukarı: 50(30(20,40),60).
+10: kökte (50) LL. 30 köke çıkar, 30'un sağ alt ağacı (40) 50'nin soluna geçer: 30(20(10), 50(40,60)).
Seviye sırası: 30 20 50 10 40 60.

Soru 7orta

Insert 40, 20, 60, 50, 70, 55 into an empty AVL tree. Which case occurs and what is the level-order traversal of the final tree?

Cevabı göster
RL at 40; 50 40 60 20 55 70

İlk beş ekleme dengeli: 40(20, 60(50,70)).
55 eklenince yol 40 → 60 → 50 → sağ. 40'ın bf'si: 0 - 2 = -2. Yeni anahtar 40'ın sağının (60) soluna gitti: RL.
Önce 60 üzerinde sağa rotasyon (50 yukarı, 55 60'ın soluna geçer), sonra 40 üzerinde sola rotasyon (50 köke çıkar).
Sonuç: 50(40(20,-), 60(55,70)). Seviye sırası 50 40 60 20 55 70.

Soru 8orta

Insert 50, 30, 70, 20, 40, 45 into an empty AVL tree. Give the level-order traversal of the final tree.

Cevabı göster
40 30 50 20 45 70

Beş ekleme sonrası: 50(30(20,40),70). 45 → 50 → 30 → 40 → sağ.
Kökün bf'si: height(30) - height(70) = 2 - 0 = 2. 45, 50'nin solunun (30) sağına gitti: LR.
30 üzerinde sola rotasyon: 40 yukarı, 30 onun solu. Sonra 50 üzerinde sağa: 40 köke çıkar, 40'ın eski sağ çocuğu 45, 50'nin soluna geçer.
Sonuç: 40(30(20), 50(45,70)).

Soru 9orta

What is the minimum number of nodes in an AVL tree of height 5? (A single node has height 0.)

Cevabı göster
20

En seyrek AVL ağacında bir alt ağaç h-1, diğeri h-2 yüksekliğindedir: N(h)=N(h−1)+N(h−2)+1N(h) = N(h-1) + N(h-2) + 1, N(0)=1N(0) = 1, N(1)=2N(1) = 2.
N(2) = 2 + 1 + 1 = 4, N(3) = 4 + 2 + 1 = 7, N(4) = 7 + 4 + 1 = 12, N(5) = 12 + 7 + 1 = 20.

Soru 10zor

Insert 5, 2, 8, 1, 4, 3 into an empty AVL tree. Where does the imbalance appear, which case is it, and what is the level-order traversal of the result?

Cevabı göster
At 5, LR case; 4 2 5 1 3 8

Beş ekleme sonrası: 5(2(1,4),8), dengeli. 3 → 5 → 2 → 4 → sol.
2'nin bf'si 0 - 1 = -1 (sorun yok). 5'in bf'si 2 - 0 = 2. 3, 5'in solunun (2) sağ alt ağacına gitti: LR.
2 üzerinde sola rotasyon: 4 yukarı, 3 2'nin sağına geçer: 4(2(1,3)). Sonra 5 üzerinde sağa: 4 köke çıkar.
Sonuç: 4(2(1,3), 5(-,8)).

Soru 11zor

Insert 12, 8, 16, 4, 10, 14, 2, 6, 1 into an empty AVL tree. Which node is rebalanced and what is the level-order traversal of the final tree?

Cevabı göster
Node 8 (LL); 12 4 16 2 8 14 1 6 10

İlk sekiz ekleme dengesizlik yaratmaz: 12(8(4(2,6),10), 16(14)).
1 → 12 → 8 → 4 → 2 → sol. Yukarı doğru bf kontrolü: 2: 1, 4: 2 - 1 = 1, 8: height(4) - height(10) = 2 - 0 = 2. İlk dengesiz düğüm 8.
1, 8'in solunun solunda: LL, 8 üzerinde tek sağa rotasyon. 4 yukarı çıkar, 4'ün sağ çocuğu 6, 8'in soluna geçer: 4(2(1), 8(6,10)).
Kök 12'nin bf'si artık 2 - 1 = 1, düzeltme bitti. Sonuç seviye sırası: 12 4 16 2 8 14 1 6 10.

Soru 12zor

Write a Java method AvlNode rotateRight(AvlNode k2) that performs a single right rotation (LL case) and returns the new subtree root. Assume AvlNode has fields key, height, left, right and a helper height(t) that returns -1 for null.

Cevabı göster
k1 = k2.left; k2.left = k1.right; k1.right = k2; update heights (k2 first); return k1

Üç işaretçi değişir, sonra yükseklikler alttan yukarı güncellenir. k2 artık k1'in çocuğu olduğu için önce k2 güncellenmeli.

static AvlNode rotateRight(AvlNode k2) {
    AvlNode k1 = k2.left;
    k2.left = k1.right;   // ortadaki alt agac (Y) k2'ye gecer
    k1.right = k2;
    k2.height = 1 + Math.max(height(k2.left), height(k2.right));
    k1.height = 1 + Math.max(height(k1.left), height(k1.right));
    return k1;            // yeni kok; cagiran t = rotateRight(t) yazar
}
Sık hata: yeni kökü döndürmeyi ya da çağıran tarafta t.left = rotateRight(t.left) atamasını unutmak. O zaman rotasyon ağaçtan kopar.

Doğrulama: Tüm ekleme izleri, rotasyon tipleri, seviye sıraları ve N(h) değerleri Java ile üretildi (verify/SEN2212/W05.java, java W05.java). Sayfadaki AVL kodu aynı dosyada çalıştırılıp anahtar tabanlı ikinci bir uygulamayla 8 dizide karşılaştırıldı, hepsi aynı çıktı.

Deftere not kartı

AVL = BST + her düğümde |height(L) - height(R)| <= 1 height(null) = -1, yaprak = 0, bf = h(L) - h(R) Ekledikten sonra yukarı çık, İLK dengesiz düğümde (en derin) düzelt, bir kez yeter bf = +2: yeni anahtar sol-sol → LL → tek SAĞA rotasyon bf = -2: sağ-sağ → RR → tek SOLA rotasyon sol-sağ → LR → önce çocukta sola, sonra düğümde sağa (çift) sağ-sol → RL → önce çocukta sağa, sonra düğümde sola (çift) Rotasyonda ortadaki alt ağaç (Y) taraf değiştirir, sıra (inorder) bozulmaz Yükseklik güncelle: önce aşağı inen düğüm, sonra yeni kök N(h) = N(h-1) + N(h-2) + 1: 1, 2, 4, 7, 12, 20, 33 Yükseklik < 1.44 log2 n → arama/ekleme/silme O(log n) 1..7 sıralı ekleme → 4 2 6 1 3 5 7 (tam ağaç) Silmede dengesizlik köke kadar birden çok kez düzeltilebilir