⚡ 7.6: Quick Sort (Hızlı Sıralama)

Pratikte En Hızlı Genel Amaçlı Sıralama

📌 Bu Bölümde Öğrenecekleriniz

🎯

Partition

Bölümleme mantığı

Neden Hızlı?

Cache-friendly

⚠️

O(n²) Riski

Pivot seçimi

🔀

Randomized

En kötüyü önle

🏆 Neden Quick Sort "Kral" Algoritma?

Quick Sort, 1960'ta Tony Hoare tarafından keşfedildi ve hala en çok kullanılan sıralama algoritmasıdır. Neden?

🖥️ Cache-Friendly

Elemanlar sıralı erişildiği için CPU cache'ini çok iyi kullanır. Merge Sort bellek atlamaları yapar, Quick Sort yapmaz.

💾 In-Place

Merge Sort O(n) ek bellek ister. Quick Sort sadece O(log n) (recursion stack). Bellek kıt olduğunda kritik!

📈 Constant Factor

Aynı O(n log n) olsa da, Quick Sort'un sabit çarpanı Merge Sort'tan ~2-3x daha küçük.

💡 Gerçek Dünya: C'nin qsort(), Java'nın Arrays.sort() (primitives için), C++'ın std::sort() Quick Sort veya varyantları kullanır!

📌 Quick Sort Nedir?

Quick Sort, bir "pivot" eleman seçip diziyi pivot etrafında bölümlere ayıran, sonra bu bölümleri recursive olarak sıralayan algoritma. Pratikte en hızlı sıralama algoritmasıdır.

🎯 Temel Fikir: Partition (Bölümleme)

  1. Bir pivot eleman seç
  2. Pivottan küçük elemanları sola topla
  3. Pivottan büyük elemanları sağa topla
  4. Pivot artık doğru yerinde! 🎯
  5. Sol ve sağ parçaları recursive sırala

🔄 Partition (Bölümleme) Adımları

Örnek: [8, 3, 7, 4, 2, 6, 5] - Pivot: 5 (son eleman)

1
Başlangıç:
8
3
7
4
2
6
5
2
Küçükleri sola, büyükleri sağa:
3
4
2
5
8
7
6

Pivot (5) artık doğru yerinde! Solunda hepsi küçük, sağında hepsi büyük.

3
Sol ve sağ parçaları recursive sırala:
2
3
4
5
6
7
8

📊 Karmaşıklık Analizi

Durum Zaman Açıklama
En İyi O(n log n) Pivot her zaman medyan
Ortalama O(n log n) Rastgele pivot iyi çalışır
En Kötü O(n²) Pivot her zaman en küçük/büyük
Alan O(log n) Call stack (in-place)

⚠️ En Kötü Durum: O(n²)

Ne zaman olur?

  • Dizi zaten sıralıysa ve son elemanı pivot seçersek
  • Dizi ters sıralıysa ve son elemanı pivot seçersek
  • Tüm elemanlar aynıysa

Çözüm: Rastgele pivot seçimi veya "median-of-three" kullanmak!

💻 Python Implementasyonu

1. Temel Versiyon (Lomuto Partition)

Python Kodu
def quick_sort(arr, low=0, high=None, depth=0):
    """
    Quick Sort - Lomuto Partition Scheme
    """
    if high is None:
        high = len(arr) - 1

    indent = "  " * depth

    if low < high:
        print(f"{indent}quick_sort(arr[{low}:{high+1}] = {arr[low:high+1]})")

        # Partition ve pivot indeksini al
        pivot_idx = partition(arr, low, high)
        print(f"{indent}  Pivot {arr[pivot_idx]} indeks {pivot_idx}'e yerleşti")
        print(f"{indent}  Dizi: {arr}")

        # Sol ve sağ parçaları recursive sırala
        quick_sort(arr, low, pivot_idx - 1, depth + 1)
        quick_sort(arr, pivot_idx + 1, high, depth + 1)

    return arr

def partition(arr, low, high):
    """
    Lomuto Partition:
    Son elemanı pivot seç, küçükleri sola topla
    """
    pivot = arr[high]  # Son eleman pivot
    i = low - 1        # Küçük elemanların sınırı

    for j in range(low, high):
        if arr[j] <= pivot:
            i += 1
            arr[i], arr[j] = arr[j], arr[i]

    # Pivotu doğru yerine koy
    arr[i + 1], arr[high] = arr[high], arr[i + 1]

    return i + 1  # Pivot'un yeni indeksi

# Test
dizi = [8, 3, 7, 4, 2, 6, 5]
print(f"Başlangıç: {dizi}\n")

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

2. Pythonic Versiyon (Basit ama O(n) EK Alan!)

⚠️ Alan Karmaşıklığı Uyarısı: Bu versiyon anlaşılması kolay olsa da O(n) ekstra bellek kullanır. Gerçek Quick Sort "in-place" çalışır ve sadece O(log n) call stack alanı kullanır. Bu versiyonu sadece öğrenmek için kullanın!
Python Kodu
def quick_sort_simple(arr):
    """
    Basit Quick Sort (List Comprehension ile)

    NOT: Bu versiyon O(n) ek alan kullanır!
    Anlaması kolay ama bellek açısından verimsiz.
    """
    if len(arr) <= 1:
        return arr

    pivot = arr[len(arr) // 2]  # Ortadaki eleman pivot

    left = [x for x in arr if x < pivot]
    middle = [x for x in arr if x == pivot]
    right = [x for x in arr if x > pivot]

    return quick_sort_simple(left) + middle + quick_sort_simple(right)

# Test
dizi = [8, 3, 7, 4, 2, 6, 5, 1, 9]
print(f"Başlangıç: {dizi}")

sonuc = quick_sort_simple(dizi)
print(f"Sonuç: {sonuc}")

# Karşılaştırma
print("\n--- Bu versiyonun artıları/eksileri ---")
print("✅ Çok okunabilir ve anlaşılır")
print("✅ Stabil (eşit elemanlar middle'da)")
print("❌ O(n) ek bellek kullanır")
print("❌ Gerçek Quick Sort in-place çalışır")
Çıktı bekleniyor...

🎯 Pivot Seçiminin Kritik Önemi

Quick Sort'un performansı tamamen pivot seçimine bağlıdır. İyi pivot = iyi performans!

📊 Medyan (Ortanca) Nedir? - Basit Anlatım

Medyan = Sayıları küçükten büyüğe sıraladığında tam ortada kalan sayı.

🎯 Örnek 1: Tek sayıda eleman
Sayılar: 3, 7, 1, 9, 5
Sıralayınca: 1, 3, 5, 7, 9
Medyan = 5 (ortadaki sayı)
🎯 Örnek 2: Çift sayıda eleman
Sayılar: 2, 8, 4, 6
Sıralayınca: 2, 4, 6, 8
Medyan = (4+6)/2 = 5 (iki ortanın ortalaması)
🤔 Neden Medyan İyi Pivot?

Medyan, tam ortada olduğu için diziyi iki eşit parçaya böler:

  • Yarısı medyandan küçük → Sol tarafa gider
  • Yarısı medyandan büyük → Sağ tarafa gider
  • Böylece her adımda problem yarıya iner → O(log n) derinlik → Hızlı!

⚠️ Problem: Gerçek medyanı bulmak için diziyi sıralamamız lazım, ama zaten sıralamak istiyoruz! Bu yüzden "Median-of-Three" kullanılır: İlk, orta ve son elemanın medyanı alınır (yaklaşık medyan).

❌ Kötü Pivot: Minimum/Maksimum

Dizi: [1, 2, 3, 4, 5], Pivot: 1

Sol: []     Sağ: [2,3,4,5]
           Sol: []  Sağ: [3,4,5]
                   Sol: []  Sağ: [4,5]
                                ...

Her adımda sadece 1 eleman atılıyor → O(n²)!

✅ İyi Pivot: Medyan

Dizi: [1, 2, 3, 4, 5], Pivot: 3

Sol: [1,2]    Sağ: [4,5]
    ↓              ↓
  [1] [2]       [4] [5]

Dizi yarıya bölünüyor → O(n log n)!

Pivot Seçim Stratejileri

Strateji Açıklama Avantaj/Dezavantaj
İlk/Son Eleman arr[0] veya arr[n-1] ⚠️ Sıralı dizide O(n²)!
Rastgele (Random) random.choice(arr) ✅ Beklenen O(n log n)
Ortanca (Median-of-Three) İlk, orta, son'un medyanı ✅ Çok güvenli, yaygın kullanılır
Ninther 9 elemanın medyanının medyanı Çok büyük diziler için
💡 Gerçek Dünya: C++'ın std::sort() "Introsort" kullanır: Quick Sort başlar, recursion derinliği log(n)'i geçerse Heap Sort'a geçer. Bu worst-case O(n²)'yi önler!

🆚 Quick Sort vs Merge Sort

Özellik Quick Sort Merge Sort
Ortalama O(n log n) O(n log n)
En Kötü O(n²) O(n log n)
Alan O(log n) O(n)
Stabil ❌ Hayır ✅ Evet
Cache ✅ Friendly ❌ Unfriendly
Pratikte Genellikle daha hızlı! Garantili performans

🎬 Quick Sort Simulasyonu

800ms
Bekleyen Pivot Karşılaştırılan Swap Yerleşti
Simulasyonu başlatmak için "Başlat" butonuna tıklayın
Quick Sort: Pivot seçilir, küçükler sola, büyükler sağa. Sonra recursive olarak sol ve sağ sıralanır.
Pivot: -
Karşılaştırma: 0
Swap: 0
Derinlik: 0

🌍 Kullanım Alanları