Heap — Yığın Ağacı

Min-Heap ve Max-Heap oluşturma, heapify-up ile ekleme, heapify-down ile çıkarma, diziden O(n) build-heap, heap sort — adım adım görseller ve C kodu.

🗂️ Veri Yapıları 📅 21 February 2026 👁️ 2 görüntülenme

Heap Nedir?

Heap (yığın), iki özelliği aynı anda sağlayan özel bir ikili ağaçtır:

1. Tam İkili Ağaç (Complete Binary Tree): Son seviye hariç tüm seviyeler tamamen dolu; son seviye soldan sağa doldurulur. Bu yapı sayesinde heap bir dizi ile verimli temsil edilir.

2. Heap Kuralı (Heap Property): Her düğüm, çocuklarından küçük (Min-Heap) veya büyük (Max-Heap) olmalıdır. Bu kural her düğümde geçerlidir — kök her zaman en küçük (veya en büyük) elemanı tutar.

BST'den farklı olarak heap'te sol-sağ ayrımı yoktur — sadece ebeveyn-çocuk ilişkisi kuralı sağlar. Ayrıca heap'te arama O(n)'dir — heap arama için değil, min/max'a hızlı erişim için optimize edilmiştir.

Min-Heap vs Max-Heap

İki Heap Türü — Aynı Elemanlar, Farklı Kural
Min-Heap: ebeveyn ≤ çocuklar
5101520302540
Kök = minimum (5) → O(1) erişim
Max-Heap: ebeveyn ≥ çocuklar
4030255201015
Kök = maksimum (40) → O(1) erişim

Dizi Tabanlı Temsil

Heap'in en güçlü yönü: tam ikili ağaç olduğu için pointer'sız, düz bir dizi ile temsil edilir. Ebeveyn-çocuk ilişkisi basit indeks formülleriyle hesaplanır:

İndeks formülleri (0-tabanlı):
Ebeveyn: (i - 1) / 2
Sol çocuk: 2*i + 1
Sağ çocuk: 2*i + 2

Ağaçta hareket etmek = dizide indeks hesaplamak. Pointer yok, malloc yok — cache-friendly.
Min-Heap: Ağaç ↔ Dizi Eşleştirmesi
5[0]10[1]15[2]20[3]30[4]25[5]40[6]
5
[0]
10
[1]
15
[2]
20
[3]
30
[4]
25
[5]
40
[6]
Ör: [1]'in ebeveyni = (1-1)/2 = [0] ✓ | [2]'nin sol çocuğu = 2×2+1 = [5] ✓

Heap Yapısı (Dizi Tabanlı)

heap.c — yapı ve yardımcılar
#include <stdio.h>
#include <stdlib.h>

#define PARENT(i)  (((i) - 1) / 2)
#define LEFT(i)    (2 * (i) + 1)
#define RIGHT(i)   (2 * (i) + 2)

typedef struct {
    int*  data;       // dinamik dizi
    int   size;       // mevcut eleman sayısı
    int   capacity;   // dizi kapasitesi
} Heap;

Heap* heapOlustur(int kapasite) {
    Heap* h = (Heap*)malloc(sizeof(Heap));
    h->data     = (int*)malloc(kapasite * sizeof(int));
    h->size     = 0;
    h->capacity = kapasite;
    return h;
}

void swap(int* a, int* b) {
    int t = *a; *a = *b; *b = t;
}
Pointer yok, dizi var: BST/AVL/RB ağaçlarında her düğüm malloc ile ayrı oluşturuluyordu. Heap'te ise tek bir ardışık dizi kullanılır — bu bellek lokasyonu açısından çok daha verimlidir. Ebeveyn/çocuk erişimi pointer takibi değil, basit aritmetik.

Eleman Ekleme — Heapify-Up

Yeni eleman dizinin sonuna eklenir (tam ikili ağaç yapısını korur), sonra heap kuralı sağlanana kadar ebeveynle yer değiştirerek yukarı tırmanır. Bu işleme "bubble-up" veya "sift-up" da denir.

Heapify-Up Algoritması (Min-Heap):
1. Elemanı dizinin sonuna ekle (index = size).
2. i = size olarak başla.
3. data[i] < data[PARENT(i)] ise → swap(i, PARENT(i)), i = PARENT(i).
4. Kural sağlanana veya köke ulaşılana kadar tekrarla.

Adım Adım — insert(3)

Mevcut Min-Heap: [5, 10, 15, 20, 30, 25, 40]. Şimdi 3 ekliyoruz.

insert(3) → Heapify-Up — 3 Adım
① Sona ekle: data[7] = 3
510152030254033<20 ✗
Dizi: [5, 10, 15, 20, 30, 25, 40, 3]
3 < 20 (ebeveyn) → swap gerekli
② swap(3, 20) → 3 yukarı, 20 aşağı
5101533<10 ✗30254020
Dizi: [5, 10, 15, 3, 30, 25, 40, 20]
3 < 10 (ebeveyn) → swap gerekli
③ swap(3, 10) → 3 yukarı, 10 aşağı
533<5 ✗151030254020
Dizi: [5, 3, 15, 10, 30, 25, 40, 20]
3 < 5 (ebeveyn) → swap gerekli
④ swap(3, 5) → 3 kök oldu ✓
35151030254020
Dizi: [3, 5, 15, 10, 30, 25, 40, 20]
3 kök → i=0, dur. Heap kuralı sağlandı ✓
heapifyUp() + heapEkle() — Min-Heap
void heapifyUp(Heap* h, int i) {
    while (i > 0 && h->data[i] < h->data[PARENT(i)]) {
        swap(&h->data[i], &h->data[PARENT(i)]);
        i = PARENT(i);
    }
}

void heapEkle(Heap* h, int deger) {
    if (h->size >= h->capacity) {
        h->capacity *= 2;
        h->data = realloc(h->data, h->capacity * sizeof(int));
    }
    h->data[h->size] = deger;     // sona ekle
    heapifyUp(h, h->size);        // yukarı düzelt
    h->size++;
}

Eleman Çıkarma — Heapify-Down

Min-Heap'ten minimum (kök) çıkarılır: kök ile son eleman yer değiştirilir, size azaltılır, sonra yeni kök heap kuralı sağlanana kadar aşağı batırılır. Her adımda daha küçük çocukla swap yapılır.

Heapify-Down Algoritması (Min-Heap):
1. Kökü (min) kaydet. Kök = son eleman. size--.
2. i = 0'dan başla.
3. En küçük çocuğu bul (sol mu sağ mı?).
4. data[i] > data[enKüçükÇocuk] ise → swap, i = enKüçükÇocuk.
5. Kural sağlanana veya yaprak olana kadar tekrarla.

Adım Adım — extractMin()

Heap: [3, 5, 15, 10, 30, 25, 40, 20]. Kök (3) çıkarılıyor.

extractMin() → Heapify-Down
① Kök(3) ↔ son(20) swap, size-- → 20 kökte, 3 çıkarıldı
Dizi: [20, 5, 15, 10, 30, 25, 40] | Çıkarılan: 3
② 20 > min(5,15)=5 → swap(20,5)
Dizi: [520, 15, 10, 30, 25, 40]
③ 20 > min(10,30)=10 → swap(20,10)
Dizi: [5, 10, 15, 20, 30, 25, 40]
④ 20 yaprak → dur. Heap kuralı sağlandı ✓
5101520302540
Sonuç: [5, 10, 15, 20, 30, 25, 40] — geçerli Min-Heap ✓
heapifyDown() + heapCikar() — Min-Heap
void heapifyDown(Heap* h, int i) {
    int enKucuk = i;

    int sol = LEFT(i);
    int sag = RIGHT(i);

    if (sol < h->size && h->data[sol] < h->data[enKucuk])
        enKucuk = sol;
    if (sag < h->size && h->data[sag] < h->data[enKucuk])
        enKucuk = sag;

    if (enKucuk != i) {
        swap(&h->data[i], &h->data[enKucuk]);
        heapifyDown(h, enKucuk);   // alt ağaçta devam
    }
}

int heapCikar(Heap* h) {
    if (h->size <= 0) {
        printf("Heap boş!\n");
        return -1;
    }
    int min = h->data[0];       // kökü kaydet
    h->data[0] = h->data[--h->size]; // son → kök
    heapifyDown(h, 0);           // aşağı düzelt
    return min;
}
Heapify-Up vs Heapify-Down: İkisi de en fazla ağaç yüksekliği kadar adım atar → O(log n). Heapify-Up yapraktan köke, Heapify-Down kökten yaprağa çalışır. Ekleme = heapify-up, çıkarma = heapify-down.

Diziden Heap İnşa Etme (Build-Heap)

Elimizde rastgele sıralı bir dizi var. Bunu heap'e dönüştürmenin iki yolu:

Naif Yöntem — O(n log n)

Boş heap'e tek tek ekle. Her ekleme O(log n), n eleman → O(n log n).

Akıllı Yöntem — O(n) 🎯

Diziyi olduğu gibi ağaç say. Son yaprak-olmayan düğümden (n/2 - 1) geriye doğru her düğüme heapify-down uygula.
Neden O(n)? Yaprak düğümler (dizinin yarısı) zaten heap kuralını sağlar — hiç işlem gerekmez. Bir üst seviyedeki düğümler en fazla 1 adım, onların üstü 2 adım… Yalnızca kök log n adım atar. Toplam iş: n/4 × 1 + n/8 × 2 + n/16 × 3 + … = O(n). Bu, geometrik seri toplamının sonucudur.

Adım Adım — Build Min-Heap

Dizi: [40, 20, 30, 10, 15, 5, 25] — 7 eleman, son yaprak-olmayan düğüm = ⌊7/2⌋ - 1 = 2. İndeks 2'den 0'a doğru heapify-down uygula.

Build Min-Heap — 3 Heapify-Down
Başlangıç — dizi olduğu gibi ağaç
40[0]20[1]30[2]10[3]15[4]5[5]25[6]
[3],[4],[5],[6] yaprak — heap kuralını otomatik sağlar.
heapifyDown(2): 30 > min(5,25)=5 → swap(30,5)
Dizi: [40, 20, 5, 10, 15, 30, 25]
heapifyDown(1): 20 > min(10,15)=10 → swap(20,10)
Dizi: [40, 10, 5, 20, 15, 30, 25]
heapifyDown(0): 40 > min(10,5)=5 → swap(40,5), sonra 40 > min(30,25)=25 → swap(40,25)
5102520153040
Sonuç: [5, 10, 25, 20, 15, 30, 40] — geçerli Min-Heap ✓ | Sadece 4 swap ile inşa!
buildMinHeap() — O(n)
void buildMinHeap(Heap* h) {
    // Son yaprak-olmayan düğümden geriye doğru
    for (int i = h->size / 2 - 1; i >= 0; i--) {
        heapifyDown(h, i);
    }
}

Max-Heap

Max-Heap, Min-Heap'in sadece karşılaştırma yönü tersine çevrilmiş halidir: ebeveyn ≥ çocuklar. Kök her zaman en büyük elemanı tutar.

Max-Heap — karşılaştırma ters
void maxHeapifyDown(int arr[], int n, int i) {
    int enBuyuk = i;
    int sol = LEFT(i);
    int sag = RIGHT(i);

    if (sol < n && arr[sol] > arr[enBuyuk])   // > (büyük)
        enBuyuk = sol;
    if (sag < n && arr[sag] > arr[enBuyuk])
        enBuyuk = sag;

    if (enBuyuk != i) {
        swap(&arr[i], &arr[enBuyuk]);
        maxHeapifyDown(arr, n, enBuyuk);
    }
}

void buildMaxHeap(int arr[], int n) {
    for (int i = n / 2 - 1; i >= 0; i--)
        maxHeapifyDown(arr, n, i);
}
Aynı Dizi → Max-Heap: [40, 20, 30, 10, 15, 5, 25]
4020301015525
Bu dizi zaten Max-Heap! 40≥20, 40≥30, 20≥10, 20≥15, 30≥5, 30≥25 ✓

Heap Sort

Heap Sort, Max-Heap yapısını kullanarak yerinde (in-place) sıralama yapar. Ek bellek gerektirmez. İki aşamadan oluşur:

Heap Sort Algoritması:
Aşama 1: Diziyi Max-Heap'e dönüştür → buildMaxHeap() — O(n)
Aşama 2: Tekrarla (n-1 kez):
  • Kök (max) ile son elemanı swap et → max sona gider (sıralı bölüme).
  • Heap boyutunu 1 azalt.
  • Yeni köke heapifyDown(0) uygula → O(log n).

Toplam: O(n) + O(n log n) = O(n log n)
Neden Max-Heap? Her adımda en büyük eleman köke gelir ve dizinin sonuna taşınır. Bu sayede sıralı bölüm dizinin sağından sola doğru büyür → artan sıralama elde edilir. Min-Heap kullanılsaydı azalan sıralama çıkardı.

Adım Adım — Heap Sort

Dizi: [40, 20, 30, 10, 15, 5, 25] — zaten Max-Heap (buildMaxHeap sonrası).

Heap Sort — Çıkarma Döngüsü
1.
swap(40,25) → heapifyDown
[30, 20, 25, 10, 15, 5 | 40]
2.
swap(30,5) → heapifyDown
[25, 20, 5, 10, 15 | 30 40]
3.
swap(25,15) → heapifyDown
[20, 15, 5, 10 | 25 30 40]
4.
swap(20,10) → heapifyDown
[15, 10, 5 | 20 25 30 40]
5.
swap(15,5) → heapifyDown
[10, 5 | 15 20 25 30 40]
6.
swap(10,5) → bitti
[5 10 15 20 25 30 40]
| işareti heap ile sıralı bölümü ayırır. Sıralı bölüm sağdan sola büyür.

Heap Sort Kodu

heapSort() — yerinde, O(n log n)
void heapSort(int arr[], int n) {
    // Aşama 1: Max-Heap inşa et — O(n)
    buildMaxHeap(arr, n);

    // Aşama 2: Tek tek çıkar — O(n log n)
    for (int i = n - 1; i > 0; i--) {
        swap(&arr[0], &arr[i]);       // max → sona
        maxHeapifyDown(arr, i, 0);   // küçülen heap'i düzelt
    }
}

Sıralama Algoritmaları Karşılaştırması

AlgoritmaEn İyiOrtalamaEn KötüAlanKararlı?
Heap SortO(n log n)O(n log n)O(n log n)O(1)Hayır ✗
Quick SortO(n log n)O(n log n)O(n²)O(log n)Hayır ✗
Merge SortO(n log n)O(n log n)O(n log n)O(n)Evet ✓
Insertion SortO(n)O(n²)O(n²)O(1)Evet ✓
Heap Sort'un avantajı: En kötü durumda bile garanti O(n log n) — Quick Sort gibi O(n²) dejenere durumu yok. Ek bellek gerektirmez (O(1) alan). Dezavantajı: cache-unfriendly (dizide sıçramalar çok) ve kararsız (eşit elemanların sırası korunmaz).

Tam Demo Program

main() — heap operasyonları + heap sort
void heapYazdir(Heap* h) {
    for (int i = 0; i < h->size; i++)
        printf("%d ", h->data[i]);
    printf("\n");
}

int main() {
    printf("=== Min-Heap Demo ===\n\n");

    // ── Tek tek ekleme ──
    Heap* h = heapOlustur(16);
    int degerler[] = {40, 20, 30, 10, 15, 5, 25};

    printf("--- Tek Tek Ekleme ---\n");
    for (int i = 0; i < 7; i++) {
        heapEkle(h, degerler[i]);
        printf("insert(%2d) → ", degerler[i]);
        heapYazdir(h);
    }

    // ── Min çıkarma ──
    printf("\n--- Min Çıkarma ---\n");
    while (h->size > 0) {
        int min = heapCikar(h);
        printf("extractMin=%2d → ", min);
        heapYazdir(h);
    }

    // ── Build-Heap + Heap Sort ──
    printf("\n--- Heap Sort ---\n");
    int arr[] = {40, 20, 30, 10, 15, 5, 25};
    int n = 7;

    printf("Orijinal: ");
    for (int i = 0; i < n; i++) printf("%d ", arr[i]);
    printf("\n");

    heapSort(arr, n);

    printf("Sıralı:   ");
    for (int i = 0; i < n; i++) printf("%d ", arr[i]);
    printf("\n");

    free(h->data); free(h);
    return 0;
}
çıktı
=== Min-Heap Demo ===

--- Tek Tek Ekleme ---
insert(40) → 40
insert(20) → 20 40
insert(30) → 20 40 30
insert(10) → 10 20 30 40
insert(15) → 10 15 30 40 20
insert( 5) → 5 15 10 40 20 30
insert(25) → 5 15 10 40 20 30 25

--- Min Çıkarma ---
extractMin= 5 → 10 15 25 40 20 30
extractMin=10 → 15 20 25 40 30
extractMin=15 → 20 30 25 40
extractMin=20 → 25 30 40
extractMin=25 → 30 40
extractMin=30 → 40
extractMin=40 →
↑ Çıkarma sırası = sıralı! (Heap Sort mantığı)

--- Heap Sort ---
Orijinal: 40 20 30 10 15 5 25
Sıralı:   5 10 15 20 25 30 40

Karmaşıklık Analizi

OperasyonZamanAçıklama
Min/Max erişimO(1)Kök = data[0]
Ekleme (insert)O(log n)Sona ekle + heapify-up
Çıkarma (extract)O(log n)Kökü al + heapify-down
Build-HeapO(n)Aşağıdan yukarı heapify
Heap SortO(n log n)Build + n extractMax
AramaO(n)Heap arama için optimize değil
AlanO(n)Dizi tabanlı — pointer yok

Heap Kullanım Alanları

AlanKullanımHeap Türü
Öncelik KuyruğuEn yüksek/düşük öncelikli elemanı hızlı çıkarMin veya Max
Dijkstra AlgoritmasıEn kısa mesafeli düğümü çıkarMin-Heap
İşletim SistemiSüreç zamanlama (öncelikli scheduling)Min-Heap
Medyan Bulmaİki heap: max-heap (sol) + min-heap (sağ)Her ikisi
Top-K ProblemiN elemandan en büyük K'yı bulMin-Heap (K boyutlu)
Huffman KodlamaMinimum frekanslı düğümleri birleştirMin-Heap

Heap vs BST vs Sıralı Dizi

OperasyonSıralı DiziBST (dengeli)Heap
Min/Max erişimO(1)O(log n)O(1) ✓
EklemeO(n)O(log n)O(log n)
Min/Max çıkarmaO(1) / O(n)O(log n)O(log n)
AramaO(log n)O(log n) ✓O(n)
Sıralı gezinmeO(n)O(n)O(n log n)
BellekDiziPointer'lı düğümDizi ✓
Doğru aracı seç: Arama önemliyse → BST/AVL. Sadece min/max'a hızlı erişim gerekiyorsa → Heap. Heap = uzmanlaşmış araç, BST = genel amaçlı araç.

Sonuç

Heap, tam ikili ağaç yapısını dizi temsili ile birleştirerek, pointer'sız ve cache-friendly bir veri yapısı sunar. Min-Heap'te kök her zaman minimum, Max-Heap'te her zaman maksimumdur — O(1) erişim. Ekleme (heapify-up) ve çıkarma (heapify-down) O(log n). Build-heap O(n) ile yerinde inşa edilir. Heap Sort ise bu mekanizmayı kullanarak garanti O(n log n) yerinde sıralama yapar.

Bölüm 6.5 özeti: Heap = tam ikili ağaç + heap kuralı (ebeveyn ≤ veya ≥ çocuklar). Dizi temsili: parent=(i-1)/2, left=2i+1, right=2i+2 — pointer yok. Ekleme: sona ekle + heapify-up → O(log n). Çıkarma: kökü al, son→kök, heapify-down → O(log n). Build-heap: son yaprak-olmayanından geriye heapifyDown → O(n). Heap Sort: buildMaxHeap + n×extractMax → O(n log n) garanti, O(1) ek alan. Min/Max erişim = O(1). Öncelik kuyruğu, Dijkstra, zamanlama, top-K, Huffman kodlama.

← Veri Yapıları