Pratikte En Hızlı Genel Amaçlı Sıralama
Bölümleme mantığı
Cache-friendly
Pivot seçimi
En kötüyü önle
Quick Sort, 1960'ta Tony Hoare tarafından keşfedildi ve hala en çok kullanılan sıralama algoritmasıdır. Neden?
Elemanlar sıralı erişildiği için CPU cache'ini çok iyi kullanır. Merge Sort bellek atlamaları yapar, Quick Sort yapmaz.
Merge Sort O(n) ek bellek ister. Quick Sort sadece O(log n) (recursion stack). Bellek kıt olduğunda kritik!
Aynı O(n log n) olsa da, Quick Sort'un sabit çarpanı Merge Sort'tan ~2-3x daha küçük.
qsort(), Java'nın Arrays.sort() (primitives için), C++'ın std::sort() Quick Sort veya varyantları kullanır!
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.
Örnek: [8, 3, 7, 4, 2, 6, 5] - Pivot: 5 (son eleman)
Pivot (5) artık doğru yerinde! Solunda hepsi küçük, sağında hepsi büyük.
| 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) |
Ne zaman olur?
Çözüm: Rastgele pivot seçimi veya "median-of-three" kullanmak!
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}")
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")
Quick Sort'un performansı tamamen pivot seçimine bağlıdır. İyi pivot = iyi performans!
Medyan = Sayıları küçükten büyüğe sıraladığında tam ortada kalan sayı.
Medyan, tam ortada olduğu için diziyi iki eşit parçaya böler:
⚠️ 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).
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²)!
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)!
| 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 |
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!
| Ö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 |