🔀 7.5: Merge Sort (Birleştirmeli Sıralama)

Böl ve Fethet ile Garantili O(n log n)

📌 Bu Bölümde Öğrenecekleriniz

✂️

Divide & Conquer

Böl ve Fethet

Garantili O(n log n)

Her zaman!

🔄

Stable

Sıra korunur

🔗

Linked List

En iyi seçim

🌟 Merge Sort Neden Önemli?

Merge Sort, John von Neumann tarafından 1945'te icat edildi ve "garantili performans" gerektiren durumlarda tercih edilir.

✅ Worst-Case Garantisi

Quick Sort O(n²)'ye düşebilir. Merge Sort HER ZAMAN O(n log n). Real-time sistemlerde bu kritik!

🔗 Linked List'te Şampiyon

Linked List'te random access yok. Quick Sort'un partition'ı kötü çalışır. Merge Sort O(1) ek alan ile çalışır!

🔄 Stability Garantisi

Eşit elemanların sırası korunur. Python'ın sorted() ve Java'nın object sort'u Merge Sort kullanır!

💡 External Sorting: RAM'e sığmayan büyük dosyaları sıralarken Merge Sort kullanılır. Disk'ten okuma chunk'lar halinde yapılır ve merge işlemi uygulanır!

📌 Merge Sort Nedir?

Merge Sort, "Böl ve Fethet" (Divide and Conquer) stratejisini kullanan verimli bir sıralama algoritmasıdır. Diziyi sürekli ikiye böler, sonra sıralı şekilde birleştirir.

🎯 Böl ve Fethet Stratejisi

✂️
1. BÖL (Divide)

Diziyi ikiye böl

⚔️
2. FETHET (Conquer)

Alt dizileri recursive sırala

🔗
3. BİRLEŞTİR (Combine)

Sıralı alt dizileri birleştir

🌳 Merge Sort Ağaç Görünümü

Örnek: [38, 27, 43, 3, 9, 82, 10]

[38, 27, 43, 3, 9, 82, 10]
↙️ BÖL ↘️
[38, 27, 43]
[3, 9, 82, 10]
↙️ BÖL ↘️
[38]
[27, 43]
[3, 9]
[82, 10]
↙️ BÖL ↘️
[38]
[27]
[43]
[3]
[9]
[82]
[10]
↗️ BİRLEŞTİR ↖️
[38]
[27, 43]
[3, 9]
[10, 82]
↗️ BİRLEŞTİR ↖️
[27, 38, 43]
[3, 9, 10, 82]
↗️ BİRLEŞTİR ↖️
[3, 9, 10, 27, 38, 43, 82]

🔗 Merge (Birleştirme) İşlemi

İki sıralı diziyi tek sıralı dizide birleştirme:

Sol Dizi

3
27
38
+

Sağ Dizi

9
10
82
=

Birleşik Dizi

3
9
10
27
38
82

Her iki diziden en küçüğü seç ve sonuca ekle → O(n)

📊 Karmaşıklık Analizi

Durum Zaman Açıklama
En İyi O(n log n) Her zaman aynı
Ortalama O(n log n) Her zaman aynı
En Kötü O(n log n) Her zaman aynı - GARANTİLİ!
Alan O(n) Geçici dizi gerektirir (dezavantaj)
📐 Neden O(n log n)?
  • log n seviye: Dizi her seferinde ikiye bölünür → log₂(n) seviye
  • Her seviyede n iş: Merge işlemi O(n)
  • Toplam: n × log n = O(n log n)

💻 Python Implementasyonu

Python Kodu
def merge_sort(arr, depth=0):
    """
    Merge Sort - Böl ve Fethet
    """
    indent = "  " * depth
    print(f"{indent}merge_sort({arr})")

    # Base case: 1 veya 0 elemanlı dizi zaten sıralı
    if len(arr) <= 1:
        return arr

    # BÖL: Ortadan ikiye ayır
    mid = len(arr) // 2
    left = arr[:mid]
    right = arr[mid:]

    print(f"{indent}  Sol: {left}, Sağ: {right}")

    # FETHET: Recursive olarak sırala
    left_sorted = merge_sort(left, depth + 1)
    right_sorted = merge_sort(right, depth + 1)

    # BİRLEŞTİR: İki sıralı diziyi birleştir
    merged = merge(left_sorted, right_sorted)
    print(f"{indent}  Birleşti: {merged}")

    return merged

def merge(left, right):
    """İki sıralı diziyi birleştirir"""
    result = []
    i = j = 0

    while i < len(left) and j < len(right):
        if left[i] <= right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1

    # Kalan elemanları ekle
    result.extend(left[i:])
    result.extend(right[j:])

    return result

# Test
dizi = [38, 27, 43, 3, 9, 82, 10]
print(f"Başlangıç: {dizi}\n")

sonuc = merge_sort(dizi)
print(f"\n{'='*40}")
print(f"Sonuç: {sonuc}")
Çıktı bekleniyor...

✅ Avantajları ve ❌ Dezavantajları

✅ Avantajları

  • Garantili O(n log n): Her durumda aynı performans
  • Stabil: Eşit elemanların sırası korunur
  • Paralel: Alt diziler bağımsız sıralanabilir
  • Linked List: Rastgele erişim gerektirmez
  • External Sort: Büyük dosyalar için ideal

❌ Dezavantajları

  • O(n) ek alan: Geçici dizi gerekli
  • Küçük diziler: Overhead var, Insertion Sort daha iyi
  • Cache-unfriendly: Bellek erişimi dağınık

🔄 Top-Down vs Bottom-Up Stratejileri

Merge Sort iki farklı yaklaşımla implemente edilebilir:

⬇️ Top-Down (Yukarıdan Aşağı)

Yaklaşım: Recursive. Büyük diziyi sürekli ikiye böl, sonra birleştir.

[8,3,7,4,2,6,5,1]
  ↓ böl
[8,3,7,4] [2,6,5,1]
  ↓ böl
[8,3] [7,4] [2,6] [5,1]
  ↓ böl
[8][3] [7][4] [2][6] [5][1]
  ↓ birleştir
[3,8] [4,7] [2,6] [1,5]
  ↓ birleştir
...

Anlaması kolay (natural recursion)

Early termination (sıralı alt dizi)

O(log n) call stack kullanır

⬆️ Bottom-Up (Aşağıdan Yukarı)

Yaklaşım: Iterative. Tek elemanlı parçalardan başla, giderek büyüt.

size=1: [8][3][7][4][2][6][5][1]
  ↓ merge
size=2: [3,8][4,7][2,6][1,5]
  ↓ merge
size=4: [3,4,7,8][1,2,5,6]
  ↓ merge
size=8: [1,2,3,4,5,6,7,8]

Stack overflow riski yok

Cache-friendly (sequential access)

Kod biraz daha karmaşık

Bottom-Up Python Implementasyonu:

Python Kodu
def merge_sort_bottom_up(arr):
    """
    Bottom-Up Merge Sort - Iterative yaklaşım
    Stack overflow riski yok!
    """
    n = len(arr)
    if n <= 1:
        return arr
    
    # Çalışma dizisi
    result = arr.copy()
    temp = [0] * n
    
    # size: merge edilecek alt dizi boyutu
    size = 1
    while size < n:
        print(f"Size {size}: {result}")
        
        # Her (size*2) blok için merge yap
        for left in range(0, n, 2 * size):
            mid = min(left + size, n)
            right = min(left + 2 * size, n)
            
            # Merge işlemi
            i, j, k = left, mid, left
            while i < mid and j < right:
                if result[i] <= result[j]:
                    temp[k] = result[i]
                    i += 1
                else:
                    temp[k] = result[j]
                    j += 1
                k += 1
            
            while i < mid:
                temp[k] = result[i]
                i += 1
                k += 1
            
            while j < right:
                temp[k] = result[j]
                j += 1
                k += 1
        
        # Sonucu kopyala
        result = temp.copy()
        size *= 2
    
    return result

# Test
dizi = [38, 27, 43, 3, 9, 82, 10]
print("Bottom-Up Merge Sort\n" + "="*30)
sonuc = merge_sort_bottom_up(dizi)
print(f"\nSonuç: {sonuc}")
Çıktı bekleniyor...
💡 Ne zaman hangisi?
  • Top-Down: Öğrenmek için, küçük-orta boyutlu dizilerde
  • Bottom-Up: Çok büyük dizilerde (stack overflow önleme), iterative tercih edildiğinde
  • Linked List: Top-Down tercih edilir (natural split)

🎬 Merge Sort Simulasyonu (Bottom-Up)

800ms
Bekleyen Sol Alt Dizi Sağ Alt Dizi Birleştirildi
Simulasyonu başlatmak için "Başlat" butonuna tıklayın
Bottom-Up Merge Sort: Tek elemanlı parçalardan başlayıp, her seviyede 2 kat büyük bloklar birleştirilir.
Seviye: 0 / 0
Blok Boyutu: 1
Karşılaştırma: 0
Merge İşlemi: 0

🌍 Kullanım Alanları