İkili Ağaçlar Binary Tree

İkili ağaç oluşturma, inorder/preorder/postorder/level-order traversal adım adım görsellerle, düğüm ekleme-silme, yükseklik-düğüm-yaprak sayısı hesaplama ve C kodu.

🗂️ Data Structures 📅 21 February 2026 👁️ 10 views

İkili Ağaç Nedir?

İkili ağaç (binary tree), her düğümün en fazla iki çocuğa sahip olduğu özel bir ağaç türüdür. Bu iki çocuk sol (left) ve sağ (right) olarak adlandırılır. Sıra önemlidir: bir düğümün sadece sol çocuğu olması ile sadece sağ çocuğu olması farklı ağaçlardır.

İkili ağaç, ağaç ailesinin en temel ve en yaygın kullanılan formudur. BST, AVL, heap, Huffman tree — hepsi ikili ağaç üzerine inşa edilmiştir. Bu bölümde ikili ağacın temel operasyonlarını öğreneceğiz: oluşturma, dolaşma (traversal), ekleme, silme ve ölçüm.

Referans Ağaç

Bu bölüm boyunca tüm örnekleri aşağıdaki 7 düğümlü ikili ağaç üzerinde göstereceğiz:

Referans İkili Ağaç (7 düğüm)
1234567
Kök: 1 | Sol: 2(→4,5) | Sağ: 3(→6,7) | Yükseklik: 2 | Yaprak: 4,5,6,7

Yapı ve Oluşturma

binary_tree.c — yapı ve oluşturma
#include <stdio.h>
#include <stdlib.h>

typedef struct Node {
    int           data;
    struct Node*  left;
    struct Node*  right;
} Node;

Node* yeniDugum(int data) {
    Node* n = (Node*)malloc(sizeof(Node));
    if (!n) { perror("malloc"); exit(1); }
    n->data  = data;
    n->left  = NULL;
    n->right = NULL;
    return n;
}

/* Referans ağacı oluştur:
 *        1
 *       / \
 *      2   3
 *     / \ / \
 *    4  5 6  7
 */
Node* referansAgac() {
    Node* r = yeniDugum(1);
    r->left        = yeniDugum(2);
    r->right       = yeniDugum(3);
    r->left->left  = yeniDugum(4);
    r->left->right = yeniDugum(5);
    r->right->left = yeniDugum(6);
    r->right->right= yeniDugum(7);
    return r;
}

Inorder Traversal (Sol → Kök → Sağ)

Inorder dolaşma, ağacı sol alt ağaç → kök → sağ alt ağaç sırasıyla ziyaret eder. İkili arama ağacında (BST) inorder dolaşma, düğümleri küçükten büyüğe sıralı olarak verir — bu yüzden en önemli dolaşma yöntemidir.

Inorder algoritması (özyinelemeli):
1. Sol alt ağacı inorder dolaş
2. Kökü ziyaret et (yazdır)
3. Sağ alt ağacı inorder dolaş

Temel durum: düğüm NULL ise geri dön.

Adım Adım — Özyineleme Takibi

Referans ağacımızda inorder dolaşmanın her özyinelemeli çağrısını takip edelim. Her satırda: hangi düğüme gidildiği, NULL'a dönüş ve veri yazdırma anı gösterilir.

Inorder: Sol → Kök → Sağ
4251637
Çıkış: 4 → 2 → 5 → 1 → 6 → 3 → 7
Özyineleme Çağrı Yığını — Her Adım:
1
inorder(1) — kök. Önce sola git.
2
inorder(2) — 1'in solu. Önce sola git.
3
inorder(4) — 2'nin solu. Önce sola git.
4
inorder(NULL) — 4'ün solu yok → geri dön
5
★ Yazdır: 4 — Sol bitti, kökü ziyaret et
[ 4 ]
6
inorder(NULL) — 4'ün sağı yok → geri dön
7
★ Yazdır: 2 — Sol(4) bitti, kökü ziyaret et
[ 4, 2 ]
8
inorder(5) — 2'nin sağı. Önce sola git.
9
inorder(NULL) — 5'in solu yok → geri dön
10
★ Yazdır: 5 — Sol bitti, kökü ziyaret et
[ 4, 2, 5 ]
11
inorder(NULL) — 5'in sağı yok → geri dön
12
★ Yazdır: 1 — Sol alt ağaç(2) bitti, kökü ziyaret et
[ 4, 2, 5, 1 ]
13
inorder(3) — 1'in sağı. Önce sola git.
14
inorder(6) — 3'ün solu. Önce sola git.
15
inorder(NULL) — 6'nın solu yok → geri dön
16
★ Yazdır: 6
[ 4, 2, 5, 1, 6 ]
17
inorder(NULL) — 6'nın sağı yok → geri dön
18
★ Yazdır: 3
[ 4, 2, 5, 1, 6, 3 ]
19
inorder(7) — 3'ün sağı.
20
inorder(NULL) — 7'nin solu yok → geri dön
21
★ Yazdır: 7
[ 4, 2, 5, 1, 6, 3, 7 ]
22
inorder(NULL) — 7'nin sağı yok → geri dön
23
Tüm çağrılar tamamlandı ✓
4 2 5 1 6 3 7
NULL kontrolü neden önemli? Her yaprak düğümün iki NULL çocuğu var. Bu NULL'lar özyinelemenin "temel durumu"dur — olmadan fonksiyon sonsuza kadar çağrılır (stack overflow). 7 düğümlü ağaçta 8 NULL kontrolü yapılır (yaprakların sol/sağ'ları + tek çocuklular).

Inorder Kodu

inorder()
void inorder(Node* node) {
    if (node == NULL) return;

    inorder(node->left);       // 1. Sol alt ağaç
    printf("%d ", node->data);  // 2. Kökü yazdır
    inorder(node->right);      // 3. Sağ alt ağaç
}
// Çıktı: 4 2 5 1 6 3 7
Neden "in-order"? Kök, sol ve sağın arasında (in the middle of) ziyaret edildiği için "inorder" adını alır. BST'de bu, elemanların sıralı (ascending) çıkmasını garanti eder çünkü sol < kök < sağ kuralı geçerlidir.

Preorder Traversal (Kök → Sol → Sağ)

Preorder dolaşma, her düğümü önce kendi, sonra sol ve sağ alt ağaçları ziyaret eder. Kök her zaman ilk yazdırılır — bu yüzden ağacın yapısını "kopyalamak" veya "serileştirmek" için idealdir. Bir ağacı dosyaya kaydetmek istiyorsanız preorder kullanırsınız.

Preorder algoritması (özyinelemeli):
1. Kökü ziyaret et (yazdır)
2. Sol alt ağacı preorder dolaş
3. Sağ alt ağacı preorder dolaş

Temel durum: düğüm NULL ise geri dön.

Ziyaret Sırası — Görsel

Preorder: Kök → Sol → Sağ
1245367
Çıkış: 1 → 2 → 4 → 5 → 3 → 6 → 7

Adım Adım — Özyineleme Takibi

Preorder: Önce yazdır, sonra sola git, sonra sağa git
1
preorder(1) — ★ Yazdır: 1 — kökü hemen yazdır
[ 1 ]
2
preorder(2) — 1'in soluna git
3
★ Yazdır: 2 — hemen yazdır, sonra sola
[ 1, 2 ]
4
preorder(4) — 2'nin soluna git
5
★ Yazdır: 4
[ 1, 2, 4 ]
6
preorder(NULL) — 4'ün solu yok → geri
7
preorder(NULL) — 4'ün sağı yok → geri
8
preorder(5) — 2'nin sağına git
9
★ Yazdır: 5
[ 1, 2, 4, 5 ]
10
preorder(NULL) — 5'in solu yok → geri
11
preorder(NULL) — 5'in sağı yok → geri
12
preorder(3) — 1'in sağına git
13
★ Yazdır: 3
[ 1, 2, 4, 5, 3 ]
14
preorder(6) — 3'ün soluna git
15
★ Yazdır: 6
[ 1, 2, 4, 5, 3, 6 ]
16
preorder(NULL) — 6'nın solu yok → geri
17
preorder(NULL) — 6'nın sağı yok → geri
18
preorder(7) — 3'ün sağına git
19
★ Yazdır: 7
[ 1, 2, 4, 5, 3, 6, 7 ]
20
preorder(NULL) — 7'nin solu yok → geri
21
preorder(NULL) — 7'nin sağı yok → geri
22
Tüm çağrılar tamamlandı ✓
1 2 4 5 3 6 7
Preorder'ın farkı: Inorder'da yazdırma sol alt ağaç bittikten sonra yapılıyordu (adım 5, 7, 10, 12...). Preorder'da yazdırma düğüme ilk ulaşıldığında yapılır (adım 1, 3, 5, 9, 13...). Bu yüzden kök her zaman çıkışta ilk elemandır.

Preorder Kodu

preorder()
void preorder(Node* node) {
    if (node == NULL) return;

    printf("%d ", node->data);  // 1. Kökü yazdır ★
    preorder(node->left);      // 2. Sol alt ağaç
    preorder(node->right);     // 3. Sağ alt ağaç
}
// Çıktı: 1 2 4 5 3 6 7
Preorder kullanım alanları: Ağacı dosyaya serileştirme/deserileştirme (ağacın tam yapısını korur), ağaç kopyalama (prefix expression), dizin yapısı listeleme (dosya sistemi tree komutu preorder kullanır).

Postorder Traversal (Sol → Sağ → Kök)

Postorder dolaşma, her düğümü en son ziyaret eder: önce sol alt ağaç, sonra sağ alt ağaç tamamen biter, ardından kök yazdırılır. Bu yüzden kök her zaman çıkışta son elemandır. Ağacı silmek (free etmek) için postorder idealdir — yapraklar önce silinir, kök en son.

Postorder algoritması (özyinelemeli):
1. Sol alt ağacı postorder dolaş
2. Sağ alt ağacı postorder dolaş
3. Kökü ziyaret et (yazdır)

Temel durum: düğüm NULL ise geri dön.

Ziyaret Sırası — Görsel

Postorder: Sol → Sağ → Kök
4526731⑦ son!
Çıkış: 4 → 5 → 2 → 6 → 7 → 3 → 1

Adım Adım — Özyineleme Takibi

Postorder: Önce sola git, sonra sağa git, en son yazdır
1
postorder(1) — kök. Yazdırmadan önce sola git.
2
postorder(2) — sola git
3
postorder(4) — sola git
4
postorder(NULL) — 4'ün solu yok → geri
5
postorder(NULL) — 4'ün sağı yok → geri
6
★ Yazdır: 4 — sol ve sağ bitti, şimdi kökü yazdır
[ 4 ]
7
postorder(5) — 2'nin sağına git
8
postorder(NULL) — 5'in solu yok → geri
9
postorder(NULL) — 5'in sağı yok → geri
10
★ Yazdır: 5
[ 4, 5 ]
11
★ Yazdır: 2 — sol(4) ve sağ(5) bitti, şimdi 2'yi yazdır
[ 4, 5, 2 ]
12
postorder(3) — 1'in sağına git
13
postorder(6) — sola git
14
postorder(NULL) → geri
15
postorder(NULL) → geri
16
★ Yazdır: 6
[ 4, 5, 2, 6 ]
17
postorder(7) — 3'ün sağına git
18
postorder(NULL) → geri
19
postorder(NULL) → geri
20
★ Yazdır: 7
[ 4, 5, 2, 6, 7 ]
21
★ Yazdır: 3 — sol(6) ve sağ(7) bitti
[ 4, 5, 2, 6, 7, 3 ]
22
★ Yazdır: 1 — sol alt ağaç(2) ve sağ alt ağaç(3) bitti → kök en son
[ 4, 5, 2, 6, 7, 3, 1 ]
23
Tüm çağrılar tamamlandı ✓
4 5 2 6 7 3 1
Postorder'ın gizli gücü: Yazdırma her zaman en son yapıldığı için, düğüm yazdırıldığında alt ağaçlarının tamamı zaten işlenmiştir. Bu özellik "aşağıdan yukarı" hesaplamaları (yükseklik, boyut, bellek toplama gibi) mümkün kılar. Ağacı silerken de postorder kullanılır: önce çocukları free et, sonra kendini — aksi halde erişilemeyen düğümler kalır (bellek sızıntısı).

Postorder Kodu

postorder()
void postorder(Node* node) {
    if (node == NULL) return;

    postorder(node->left);     // 1. Sol alt ağaç
    postorder(node->right);    // 2. Sağ alt ağaç
    printf("%d ", node->data);  // 3. Kökü yazdır ★
}
// Çıktı: 4 5 2 6 7 3 1

Üç DFS Traversal — Karşılaştırma

Inorder, preorder ve postorder aynı DFS (depth-first search) mantığını kullanır — fark sadece yazdırma satırının yeridir:

Tek Fark: printf'in Yeri
Preorder
★ printf
recurse(left)
recurse(right)
Inorder
recurse(left)
★ printf
recurse(right)
Postorder
recurse(left)
recurse(right)
★ printf
TraversalSıraKök pozisyonuÇıktıTemel kullanım
PreorderKök → Sol → Sağİlk1 2 4 5 3 6 7Serileştirme, kopyalama
InorderSol → Kök → SağOrtada4 2 5 1 6 3 7BST sıralı çıktı
PostorderSol → Sağ → KökSon4 5 2 6 7 3 1Silme, yükseklik hesaplama
Hatırlatma formülü: "pre" = önce yazdır, "in" = arada yazdır, "post" = sonra yazdır. Yazdırmanın yeri = traversal adı. Üçünde de sol her zaman sağ'dan önce gelir.

Level-Order Traversal (Seviye Sırasıyla — BFS)

Önceki üç dolaşma (inorder, preorder, postorder) hep derinlik öncelikli (DFS — Depth-First Search) mantığıyla çalışıyordu: bir dalı sonuna kadar takip et, sonra geri dön. Level-order ise tamamen farklı bir strateji kullanır: genişlik öncelikli (BFS — Breadth-First Search). Ağacı seviye seviye, soldan sağa dolaşır — tıpkı bir kitabı satır satır okumak gibi.

Level-order algoritması:
1. Kökü bir kuyruğa (queue) ekle.
2. Kuyruk boş olana kadar tekrarla:
 a. Kuyruktan bir düğüm çıkar (dequeue), yazdır.
 b. Sol çocuğu varsa kuyruğa ekle (enqueue).
 c. Sağ çocuğu varsa kuyruğa ekle (enqueue).

Özyineleme kullanmaz! Kuyruk (FIFO) doğal olarak seviye sırasını korur.
DFS vs BFS — Temel Fark: DFS stack kullanır (özyineleme = implicit stack). BFS queue kullanır. Stack son gireni önce çıkarır → derinliğe iner. Queue ilk gireni önce çıkarır → aynı seviyeyi bitirir, sonra aşağıya geçer.

Ziyaret Sırası — Seviye Seviye

Level-Order: Seviye 0 → Seviye 1 → Seviye 2
Seviye 0Seviye 1Seviye 21234567
Çıkış: 1 → 2 → 3 → 4 → 5 → 6 → 7

Adım Adım — Kuyruk Durumu Takibi

Her adımda kuyruktan bir düğüm çıkarılır, çocukları kuyruğa eklenir. Kuyruğun FIFO yapısı seviye sırasını doğal olarak korur:

Level-Order — 7 Adımda Kuyruk Durumu
Başlangıç: Kökü kuyruğa ekle
Kuyruk:1
Çıktı: (henüz yok)
Adım 1: Dequeue → 1 | Enqueue → sol:2, sağ:3
Kuyruk:23
Çıktı: [ 1 ]
Adım 2: Dequeue → 2 | Enqueue → sol:4, sağ:5
Kuyruk:345
Çıktı: [ 1, 2 ]
3 daha önce eklenmişti → 4,5'ten önce çıkacak (FIFO) → seviye sırası korunuyor!
Adım 3: Dequeue → 3 | Enqueue → sol:6, sağ:7
Kuyruk:4567
Çıktı: [ 1, 2, 3 ]
Seviye 1 tamamlandı ✓ Kuyrukta sadece Seviye 2 düğümleri kaldı
Adım 4: Dequeue → 4 | Çocuğu yok → enqueue yapılmaz
Kuyruk:567
Çıktı: [ 1, 2, 3, 4 ]
Adım 5: Dequeue → 5 | Çocuğu yok
Kuyruk:67
Çıktı: [ 1, 2, 3, 4, 5 ]
Adım 6: Dequeue → 6 | Çocuğu yok
Kuyruk:7
Çıktı: [ 1, 2, 3, 4, 5, 6 ]
Adım 7: Dequeue → 7 | Çocuğu yok → Kuyruk boş → BİTTİ ✓
Kuyruk:[ boş ]
Çıktı: [ 1, 2, 3, 4, 5, 6, 7 ] ✓
FIFO'nun büyüsü: Adım 2'de 3, kuyruğa 4 ve 5'ten önce eklenmişti. FIFO kuralı 3'ü önce çıkardı — böylece Seviye 1'deki tüm düğümler Seviye 2'den önce tamamlandı. Hiçbir özyineleme veya derinlik kontrolü gerekmedi — kuyruk yapısı bunu otomatik sağladı.

Level-Order Kodu

levelOrder() — Kuyruk ile BFS
/* ── Basit kuyruk (level-order için) ── */
#define QMAX 100
typedef struct {
    Node* items[QMAX];
    int   front, rear, count;
} Queue;

void  qInit(Queue* q)          { q->front=0; q->rear=-1; q->count=0; }
int   qEmpty(Queue* q)         { return q->count==0; }
void  enqueue(Queue* q, Node* n) {
    q->rear = (q->rear+1) % QMAX;
    q->items[q->rear] = n;
    q->count++;
}
Node* dequeue(Queue* q) {
    Node* n = q->items[q->front];
    q->front = (q->front+1) % QMAX;
    q->count--;
    return n;
}

/* ── Level-Order Traversal ── */
void levelOrder(Node* root) {
    if (!root) return;

    Queue q;
    qInit(&q);
    enqueue(&q, root);

    while (!qEmpty(&q)) {
        Node* cur = dequeue(&q);
        printf("%d ", cur->data);       // yazdır

        if (cur->left)  enqueue(&q, cur->left);
        if (cur->right) enqueue(&q, cur->right);
    }
    printf("\n");
}
// Çıktı: 1 2 3 4 5 6 7

DFS vs BFS — Dört Traversal Karşılaştırması

ÖzellikDFS (In/Pre/Post)BFS (Level-Order)
Veri yapısıStack (özyineleme)Queue
StratejiDerinliğe dalışGenişliğe yayılma
Bellek (dengeli)O(h) = O(log n)O(w) — w: max seviye genişliği
Bellek (dejenere)O(n)O(1)
Bellek (perfect)O(log n)O(n/2) = O(n)
Doğal sıralamaInorder → BST sıralıSeviye sıralı
En kısa yolGaranti etmezGaranti eder
İmplementasyonBasit (3 satır recursive)Kuyruk yönetimi gerekir

DFS kullan:

BST sıralı çıktı (inorder).
Ağaç serileştirme (preorder).
Ağaç silme / yükseklik hesaplama (postorder).
Bellek kısıtlıysa ve ağaç genişse.

BFS kullan:

Seviye seviye işleme / yazdırma.
En yakın düğümü bulma.
Minimum derinlikteki yaprağı bulma.
Ağacı dizi gösterimine dönüştürme.
4 traversal özeti:
Preorder: 1 2 4 5 3 6 7 — kök ilk
Inorder: 4 2 5 1 6 3 7 — BST'de sıralı
Postorder: 4 5 2 6 7 3 1 — kök son
Level-order: 1 2 3 4 5 6 7 — seviye seviye

Düğüm Ekleme (Insertion)

Genel ikili ağaçta (BST değil!) düğüm ekleme kuralı basittir: ağacı complete (eksiksiz) tutmak için yeni düğüm level-order'da ilk boş konuma eklenir. BFS ile ağacı dolaşıp sol veya sağ çocuğu NULL olan ilk düğümü buluruz.

Ekleme algoritması:
1. Ağaç boşsa → yeni düğüm kök olur.
2. Değilse → BFS ile dolaş.
3. Sol çocuğu NULL olan ilk düğümü bul → sol çocuk olarak ekle.
4. Sol dolu ama sağ NULL ise → sağ çocuk olarak ekle.
5. İkisi de doluysa → sonraki düğüme geç (BFS devam).

Ekleme Görseli — insert(8)

insert(8) → Level-order'da ilk boş yer bulunur
Önce: 7 düğümlü tam (perfect) ağaç
1234567
Sonra: BFS → 1(dolu) → 2(dolu) → 3(dolu) → 4(sol boş!) → 8 eklendi
12345678YENİ
Complete yapı korundu: yeni seviyenin en solu dolduruldu

Ekleme Kodu

insert() — BFS ile ilk boş yeri bul
Node* insert(Node* root, int deger) {
    Node* yeni = yeniDugum(deger);
    if (!root) return yeni;

    Queue q;
    qInit(&q);
    enqueue(&q, root);

    while (!qEmpty(&q)) {
        Node* cur = dequeue(&q);

        if (!cur->left) {
            cur->left = yeni;  // ilk boş sol → ekle
            return root;
        } else {
            enqueue(&q, cur->left);
        }

        if (!cur->right) {
            cur->right = yeni; // ilk boş sağ → ekle
            return root;
        } else {
            enqueue(&q, cur->right);
        }
    }
    return root; // ulaşılmaz (güvenlik)
}
// Karmaşıklık: O(n) — en kötüde tüm düğümleri dolaşır

Düğüm Silme (Deletion)

İkili ağaçta düğüm silme, eklemeye göre çok daha karmaşıktır. Silinecek düğümün çocukları varsa, ağaç yapısı bozulmamalıdır. Genel ikili ağaçta (BST değil!) kullanılan strateji:

Silme stratejisi:
1. Silinecek düğümü BFS ile bul.
2. Ağacın en derin, en sağdaki düğümünü (deepest rightmost) BFS ile bul.
3. Silinecek düğümün verisini en derin düğümün verisiyle değiştir (swap).
4. En derin düğümü sil (yaprak olacağı için basitçe kaldır).

Bu strateji ağacı complete olmaya yakın tutar ve yapıyı korur.
Neden bu strateji? Silinecek düğüm iç düğüm (internal) olabilir — onu direkt kaldırmak çocuklarını öksüz bırakır. En derin sağdaki düğümle takas edersek, artık silinen düğüm yaprak olur → basitçe kaldırılır. Bu strateji BST sıralamasını korumaz (gerek yok — BST değiliz), ama ağaç yapısını sağlam tutar.

Silme Senaryosu — delete(2)

delete(2) — 4 Adımlık Silme İşlemi
① Mevcut ağaç — 2'yi silmek istiyoruz
12SİL34567
② BFS ile en derin, en sağdaki düğümü bul → 7
BFS sırası: 1→2→3→4→5→6→7 (son çıkan = en derin sağdaki)
③ Swap: 2'nin verisini 7 ile değiştir
17←swap34562→SİL
2 artık yaprak pozisyonunda → güvenle silinebilir
④ Yaprak düğümü (eski 7 pozisyonu) sil → free()
173456
6 düğüm kaldı, 2'nin verisi gitti ama ağaç yapısı sağlam ✓

Silme Kodu

deleteNode() — en derin düğümle takas + sil
/* ── En derin, en sağdaki düğümü sil ── */
void enDerinSil(Node* root, Node* hedef) {
    Queue q;
    qInit(&q);
    enqueue(&q, root);

    while (!qEmpty(&q)) {
        Node* cur = dequeue(&q);

        // Hedefin ebeveynini bul ve bağını kes
        if (cur->left) {
            if (cur->left == hedef) {
                cur->left = NULL;
                free(hedef);
                return;
            }
            enqueue(&q, cur->left);
        }
        if (cur->right) {
            if (cur->right == hedef) {
                cur->right = NULL;
                free(hedef);
                return;
            }
            enqueue(&q, cur->right);
        }
    }
}

/* ── Düğüm silme ── */
Node* deleteNode(Node* root, int deger) {
    if (!root) return NULL;

    // Tek düğümlü ağaç
    if (!root->left && !root->right) {
        if (root->data == deger) {
            free(root);
            return NULL;
        }
        return root;
    }

    Queue q;
    qInit(&q);
    enqueue(&q, root);

    Node* hedef = NULL;   // silinecek düğüm
    Node* enDerin = NULL; // en derin sağdaki

    while (!qEmpty(&q)) {
        enDerin = dequeue(&q);

        if (enDerin->data == deger)
            hedef = enDerin;

        if (enDerin->left)  enqueue(&q, enDerin->left);
        if (enDerin->right) enqueue(&q, enDerin->right);
    }
    // Döngü bittiğinde enDerin = son dequeue edilen = en derin sağdaki

    if (hedef) {
        hedef->data = enDerin->data;  // veriyi takas et
        enDerinSil(root, enDerin);     // yaprak düğümü sil
    }
    return root;
}
// Karmaşıklık: O(n) — iki BFS geçişi

Ağacı Tamamen Silme (Bellek Temizleme)

Ağacın tüm düğümlerini free etmek için postorder kullanılır: önce çocukları sil, sonra kendini. Bu sıra kritiktir — preorder ile silmeye çalışırsanız çocuklara erişim kaybedersiniz.

agaciSil() — postorder ile bellek temizleme
void agaciSil(Node* node) {
    if (!node) return;

    agaciSil(node->left);   // 1. Sol alt ağacı sil
    agaciSil(node->right);  // 2. Sağ alt ağacı sil
    free(node);              // 3. Kendini sil (postorder!)
}
// Silme sırası: 4→5→2→6→7→3→1 (postorder)
Silme özeti: Tek düğüm silme → en derin sağdakiyle takas + yaprak sil → O(n). Tüm ağacı silme → postorder traversal ile free → O(n). Her iki durumda da bellek sızıntısı oluşmaz ✓

Ağaç Yüksekliği Bulma

Ağaç yüksekliği, kökten en derin yaprağa kadar olan kenar sayısıdır. Özyinelemeli hesaplama mantığı: her düğümün yüksekliği = max(sol yükseklik, sağ yükseklik) + 1. NULL düğümün yüksekliği -1'dir (kenar sayısı tanımı).

Yükseklik algoritması (postorder mantığı):
1. NULL ise → -1 döndür (temel durum).
2. Sol yükseklik = height(left)
3. Sağ yükseklik = height(right)
4. max(sol, sağ) + 1 döndür.

Aşağıdan Yukarı Hesaplama

Her Düğümde Yükseklik Hesabı (aşağıdan yukarıya)
4h=05h=06h=07h=02h=max(0,0)+1=13h=max(0,0)+1=11h=max(1,1)+1=2
Yapraklar (h=0) → iç düğümler (h=1) → kök (h=2) — postorder mantığı!
Özyineleme açılımı (postorder sırası):
1
height(4→left=NULL) → -1
2
height(4→right=NULL) → -1
3
height(4) = max(-1,-1)+1 = 0
yaprak → 0
4
height(5) = max(-1,-1)+1 = 0
yaprak → 0
5
height(2) = max(0,0)+1 = 1
iç → 1
6
height(6) = 0, height(7) = 0
yapraklar
7
height(3) = max(0,0)+1 = 1
iç → 1
8
height(1) = max(1,1)+1 = 2
kök → 2 ✓
height()
int height(Node* node) {
    if (!node) return -1;  // NULL → -1 (kenar sayısı)

    int solH = height(node->left);
    int sagH = height(node->right);

    return (solH > sagH ? solH : sagH) + 1;
}
// Referans ağaç → height = 2
// Karmaşıklık: O(n) — her düğüm tam 1 kez ziyaret edilir

Düğüm Sayısı Hesaplama

Ağaçtaki toplam düğüm sayısı: 1 + sol_sayı + sağ_sayı. NULL ise 0.

Düğüm Sayısı — Aşağıdan Yukarı Toplama
4n=15n=16n=17n=121+1+1=331+1+1=311+3+3=7
dugumSayisi()
int dugumSayisi(Node* node) {
    if (!node) return 0;
    return 1 + dugumSayisi(node->left)
               + dugumSayisi(node->right);
}
// Referans ağaç → 7
// Karmaşıklık: O(n)

Yaprak Sayısı Hesaplama

Yaprak, sol ve sağ çocuğu NULL olan düğümdür. Sayma mantığı: yaprak ise 1, değilse sol_yaprak + sağ_yaprak.

Yaprak Sayısı — Sadece Uç Düğümler Sayılır
4yaprak ✓5yaprak ✓6yaprak ✓7yaprak ✓2iç → 0+2=23iç → 0+2=21iç → 2+2=4
4 yaprak: {4, 5, 6, 7}
yaprakSayisi()
int yaprakSayisi(Node* node) {
    if (!node) return 0;
    if (!node->left && !node->right)
        return 1;  // yaprak!
    return yaprakSayisi(node->left)
         + yaprakSayisi(node->right);
}
// Referans ağaç → 4
// Karmaşıklık: O(n)

Tam Demo Program

main() — tüm operasyonlar
int main() {
    Node* root = referansAgac();

    printf("=== İkili Ağaç Demo ===\n\n");

    printf("Inorder:     "); inorder(root);    printf("\n");
    printf("Preorder:    "); preorder(root);   printf("\n");
    printf("Postorder:   "); postorder(root);  printf("\n");
    printf("Level-order: "); levelOrder(root);

    printf("\nYükseklik:    %d\n", height(root));
    printf("Düğüm sayısı: %d\n", dugumSayisi(root));
    printf("Yaprak sayısı: %d\n", yaprakSayisi(root));

    printf("\n--- insert(8) ---\n");
    root = insert(root, 8);
    printf("Level-order: "); levelOrder(root);
    printf("Düğüm sayısı: %d\n", dugumSayisi(root));

    printf("\n--- delete(2) ---\n");
    root = deleteNode(root, 2);
    printf("Level-order: "); levelOrder(root);
    printf("Düğüm sayısı: %d\n", dugumSayisi(root));

    agaciSil(root);
    printf("\nAğaç silindi (bellek temiz).\n");
    return 0;
}
çıktı
=== İkili Ağaç Demo ===

Inorder:     4 2 5 1 6 3 7
Preorder:    1 2 4 5 3 6 7
Postorder:   4 5 2 6 7 3 1
Level-order: 1 2 3 4 5 6 7

Yükseklik:    2
Düğüm sayısı: 7
Yaprak sayısı: 4

--- insert(8) ---
Level-order: 1 2 3 4 5 6 7 8
Düğüm sayısı: 8

--- delete(2) ---
Level-order: 1 8 3 4 5 6 7
Düğüm sayısı: 7

Ağaç silindi (bellek temiz).

Karmaşıklık Özeti

OperasyonZamanAlanYöntem
Inorder traversalO(n)O(h)DFS — özyineleme (stack)
Preorder traversalO(n)O(h)DFS — özyineleme (stack)
Postorder traversalO(n)O(h)DFS — özyineleme (stack)
Level-order traversalO(n)O(w)BFS — kuyruk (queue)
Ekleme (insert)O(n)O(w)BFS — ilk boş yer
Silme (delete)O(n)O(w)BFS — takas + yaprak sil
YükseklikO(n)O(h)Postorder özyineleme
Düğüm sayısıO(n)O(h)Postorder özyineleme
Yaprak sayısıO(n)O(h)Postorder özyineleme
Ağacı silmeO(n)O(h)Postorder — free()
n vs h vs w: n = toplam düğüm, h = yükseklik (dengeli: log n, dejenere: n), w = en geniş seviyedeki düğüm sayısı (perfect ağaçta: n/2). DFS alan = O(h), BFS alan = O(w).

Sonuç

İkili ağaç, hiyerarşik veri yapılarının temel taşıdır. Bu bölümde 10 temel operasyonu öğrendik: oluşturma, 4 dolaşma yöntemi (inorder, preorder, postorder, level-order), ekleme, silme ve 3 ölçüm (yükseklik, düğüm sayısı, yaprak sayısı). Özyinelemenin (recursion) ağaç operasyonlarındaki gücünü gördük — neredeyse tüm fonksiyonlar 5 satırın altında.

Tüm operasyonların O(n) olduğuna dikkat edin — genel ikili ağaç yapısı arama için optimize değildir. Bir sonraki bölümde İkili Arama Ağacı (BST)'nı tanıyacağız: sol < kök < sağ kuralı sayesinde arama, ekleme ve silme O(h)'ye düşer. Dengeli bir BST'de h = O(log n) — bu da 1.000.000 elemanda sadece ~20 adım demektir.

Bölüm 6.2 özeti: İkili ağaç = max 2 çocuk/düğüm. Dolaşma: preorder (kök-sol-sağ), inorder (sol-kök-sağ), postorder (sol-sağ-kök), level-order (BFS+kuyruk). Ekleme: BFS ile ilk boş yer. Silme: en derin sağdakiyla takas + yaprak sil. Ölçüm: yükseklik max(sol,sağ)+1, düğüm 1+sol+sağ, yaprak sol+sağ. Tüm operasyonlar O(n). Özyineleme = ağacın doğal dili.
← Data Structures