🌳 7.7: Heap Sort (Yığın Sıralaması)

Heap Veri Yapısını Kullanarak Garantili O(n log n) Sıralama

📌 Bu Bölümde Öğrenecekleriniz

🌳

Heap Nedir?

Özel ağaç yapısı

📐

O(n log n)

Her zaman!

💾

O(1) Alan

In-place sıralama

🔄

Heapify

Anahtar işlem

🤔 Heap Sort Nedir?

Heap Sort, heap (yığın) veri yapısını kullanarak sıralama yapan bir algoritmadır. Hem zaman hem de alan açısından verimlidir.

💡 Heap Nedir?

Heap, özel bir tam ikili ağaç (complete binary tree) yapısıdır:

  • Max-Heap: Her düğüm, çocuklarından büyük veya eşit (kök en büyük)
  • Min-Heap: Her düğüm, çocuklarından küçük veya eşit (kök en küçük)

Sıralama için genellikle Max-Heap kullanılır.

🌲 Heap'in Dizi Temsili

Heap, bir dizi olarak saklanabilir! Tam ikili ağaç olduğu için:

  • Kök: index 0
  • Sol çocuk: 2*i + 1
  • Sağ çocuk: 2*i + 2
  • Ebeveyn: (i - 1) // 2
Ağaç görünümü:
        90
       /  \
     80    70
    / \   /
   40 50 60
Dizi: [90, 80, 70, 40, 50, 60]

🎬 Algoritma Adımları

Aşama 1: Build Max-Heap (Heap Oluştur)

Diziyi max-heap yapısına dönüştür. Son yaprak olmayan düğümden başlayarak heapify uygula.

🕒 Zaman: O(n) - Linear! (Her düğüm için O(log n) değil, toplamda O(n) çıkıyor)

Aşama 2: Sıralama (Extract Max)

  1. Kökü (en büyük) dizinin sonuna taşı
  2. Heap boyutunu 1 azalt
  3. Yeni kökte heapify uygula
  4. Heap boşalana kadar tekrarla
🕒 Zaman: O(n log n) - n kez extract, her biri O(log n)

🔄 Heapify Nedir?

Heapify, bir düğümü alt ağacı için doğru pozisyona "batıran" (sift-down) işlemdir.

Heapify Adımları:

  1. Düğümü sol ve sağ çocukları ile karşılaştır
  2. En büyüğü düğümün kendisi değilse, en büyük çocukla yer değiştir
  3. Değişim yapıldıysa, alt ağaçta recursive olarak heapify
  4. Değişim yapılmadıysa dur (heap özelliği sağlandı)

❌ Heapify Öncesi

       10  ← Bu düğüm heap bozuyor
      /  \
    80    70

10, çocuklarından küçük!

✅ Heapify Sonrası

       80  ← En büyük köke geldi
      /  \
    10    70

Max-heap özelliği sağlandı!

👁️ Adım Adım Heap Sort Örneği

Diziyi sıralayalım: [4, 10, 3, 5, 1]

Aşama 1: Build Max-Heap

Başlangıç: [4, 10, 3, 5, 1]
Heapify index 1 (10): Zaten ok
Heapify index 0 (4): 4 < 10 → swap → [10, 4, 3, 5, 1]
Heapify index 1 (4): 4 < 5 → swap → [10, 5, 3, 4, 1]
Max-Heap: [10, 5, 3, 4, 1]

Aşama 2: Sıralama (Extract Max)

Adım 1: Swap root(10) ↔ last(1) → [1, 5, 3, 4, 10]
Heapify → [5, 4, 3, 1, 10]
Adım 2: Swap root(5) ↔ last(1) → [1, 4, 3, 5, 10]
Heapify → [4, 1, 3, 5, 10]
Adım 3: Swap root(4) ↔ last(3) → [3, 1, 4, 5, 10]
Heapify → [3, 1, 4, 5, 10]
Adım 4: Swap root(3) ↔ last(1) → [1, 3, 4, 5, 10]
Sıralı Dizi: [1, 3, 4, 5, 10] ✅

📊 Karmaşıklık Analizi

⏱️ Zaman

O(n log n)

Her durumda garantili!

💾 Alan

O(1)

In-place sıralama

🔄 Stable?

❌ Hayır

Eşit elemanların sırası değişebilir

🤔 Quick Sort vs Heap Sort

Quick Sort Heap Sort
En Kötü O(n²) O(n log n)
Pratikte Daha hızlı (cache friendly) Biraz yavaş
Alan O(log n) stack O(1)

💻 Python Implementasyonu

Heap Sort - Python
# ====================================
# HEAP SORT ALGORİTMASI
# ====================================

def heapify(arr, n, i):
    """
    i indeksindeki düğümü max-heap özelliğine uygun hale getir.
    
    Parametreler:
    - arr: Dizi
    - n: Heap boyutu (sıralama sırasında azalır)
    - i: Heapify yapılacak düğüm indeksi
    """
    largest = i          # Başlangıçta kök en büyük varsayılır
    left = 2 * i + 1     # Sol çocuk indeksi
    right = 2 * i + 2    # Sağ çocuk indeksi
    
    # Sol çocuk daha büyükse
    if left < n and arr[left] > arr[largest]:
        largest = left
    
    # Sağ çocuk daha büyükse
    if right < n and arr[right] > arr[largest]:
        largest = right
    
    # En büyük kök değilse swap yap ve recursive heapify
    if largest != i:
        arr[i], arr[largest] = arr[largest], arr[i]
        print(f"  Swap: arr[{i}]={arr[largest]} ↔ arr[{largest}]={arr[i]}")
        heapify(arr, n, largest)

def heap_sort(arr):
    """
    Heap Sort Ana Fonksiyonu
    
    Zaman Karmaşıklığı: O(n log n) - Her durumda
    Alan Karmaşıklığı: O(1) - In-place
    Stable: Hayır
    """
    n = len(arr)
    
    # AŞAMA 1: Build Max-Heap
    print("=" * 50)
    print("AŞAMA 1: Build Max-Heap")
    print("=" * 50)
    
    # Son yaprak olmayan düğümden başla: n//2 - 1
    for i in range(n // 2 - 1, -1, -1):
        print(f"\nHeapify index {i} (değer: {arr[i]})")
        heapify(arr, n, i)
        print(f"Dizi: {arr}")
    
    print(f"\n✅ Max-Heap oluşturuldu: {arr}")
    
    # AŞAMA 2: Sıralama - Kökü sona taşı
    print("\n" + "=" * 50)
    print("AŞAMA 2: Sıralama (Extract Max)")
    print("=" * 50)
    
    for i in range(n - 1, 0, -1):
        # Kökü (en büyük) sona taşı
        arr[0], arr[i] = arr[i], arr[0]
        print(f"\nSwap root({arr[i]}) ↔ arr[{i}]({arr[0]})")
        
        # Yeni kökte heapify (boyut azaltılmış heap için)
        heapify(arr, i, 0)
        print(f"Dizi: {arr[:i]} | Sıralı: {arr[i:]}")
    
    return arr

# ============ TEST ============
print("🌳 HEAP SORT")
print("=" * 50)

dizi = [4, 10, 3, 5, 1]
print(f"Başlangıç: {dizi}\n")

heap_sort(dizi)
print(f"\n🎉 Sonuç: {dizi}")
Çıktı bekleniyor...

✅ Avantajları ve ❌ Dezavantajları

✅ Avantajları

  • Garantili O(n log n): Quick Sort gibi en kötü durumda kötüleşmez
  • O(1) alan: Ek bellek gerekmez
  • Priority Queue: Heap yapısı çok kullanışlı
  • K en büyük/küçük: Tamamını sıralamadan bulabilir

❌ Dezavantajları

  • Cache unfriendly: Rastgele erişim pattern'i
  • Stable değil: Eşit elemanların sırası değişebilir
  • Pratikte yavaş: Quick Sort genellikle daha hızlı
  • Recursive overhead: Heapify recursive

🎯 Ne Zaman Heap Sort Kullanılır?

🐍 Python'da heapq Modülü

Python'un heapq modülü min-heap implementasyonu sunar. Heap Sort yazmak yerine bunu kullanabilirsiniz!

heapq Kullanımı
import heapq

# heapq ile sıralama
dizi = [4, 10, 3, 5, 1]
print(f"Orijinal: {dizi}")

# Yöntem 1: heapify + pop
heap = dizi.copy()
heapq.heapify(heap)  # O(n)
print(f"Min-Heap: {heap}")

sirali = []
while heap:
    sirali.append(heapq.heappop(heap))
print(f"Sıralı: {sirali}")

# Yöntem 2: nsmallest / nlargest
print(f"\nEn küçük 3: {heapq.nsmallest(3, dizi)}")
print(f"En büyük 3: {heapq.nlargest(3, dizi)}")

# Priority Queue olarak kullanım
print("\n--- Priority Queue ---")
pq = []
heapq.heappush(pq, (3, "Görev C"))
heapq.heappush(pq, (1, "Görev A"))
heapq.heappush(pq, (2, "Görev B"))

while pq:
    priority, task = heapq.heappop(pq)
    print(f"  Öncelik {priority}: {task}")
Çıktı bekleniyor...

📋 Bölüm Özeti

🌳 Algoritma

Build Max-Heap, sonra extract max

⏱️ Zaman

O(n log n) - Garantili

💾 Alan

O(1) - In-place

✨ Özellik

Unstable, Top-K için ideal

📚 Daha Fazla Bilgi: Heap Veri Yapısı

Heap veri yapısı, sıralama dışında Priority Queue ve Top-K problemleri gibi birçok alanda kullanılır.

Heap veri yapısı hakkında daha detaylı bilgi için Bölüm 9c: Heap Sort Detayları sayfasına göz atabilirsiniz. O bölümde:

  • Heap özelliklerinin (heap property) detaylı analizi
  • Min-Heap vs Max-Heap karşılaştırması
  • Priority Queue implementasyonu
  • Heap'in gerçek dünya uygulamaları

🎬 Heap Sort Simulasyonu

800ms

📊 Dizi Görünümü

🌳 Heap Ağacı (Max-Heap)

* Heapify işlemi sırasında ağaç yapısı değişir
Heap Kök (Max) Heapify Sıralanmış
Simulasyonu başlatmak için "Başlat" butonuna tıklayın
Heap Sort: Önce Max-Heap oluşturulur, sonra kök (en büyük) çıkarılıp sona konur.
Aşama: Bekleniyor
Heap Boyutu: 0
Karşılaştırma: 0
Swap: 0