Bölüm 2: Sıralama Algoritmaları
Giriş
Öğrenme Çıktıları
Sıralamayı; zaman, ek bellek, kararlılık, yerinde çalışma ve karşılaştırmalı / karşılaştırmasız olma ölçütleriyle değerlendirmek.
Kabarcık, eklemeli, seçmeli ve kabuk sıralamayı bir dizi üzerinde adım adım izlemek ve Java ile yazmak.
Birleştirmeli, hızlı ve yığın sıralamanın çalışmasını açıklamak; O(n log n) ve en kötü durum davranışlarını gerekçelendirmek.
Karşılaştırma alt sınırını, saymalı ve basamak sıralamayı açıklamak; bir problem için uygun algoritmayı seçmek.
Giriş
Sıralama Algoritmaları
- Alışveriş listelerinden sosyal medya gönderilerine kadar dijital dünyada sürekli veri ile karşılaşırız.
- Verinin etkin kullanılabilmesi için düzenli tutulması gerekir.
- Sıralama algoritmaları (sorting algorithms) bir listedeki öğeleri belirli bir ölçüte göre düzenler:
- sayısal değer,
- alfabetik sıra,
- tarih …
- Sıra artan (küçükten büyüğe) ya da azalan olabilir.
Giriş
Sıralama Algoritmalarının Çeşitleri
Farklı sıralama algoritmaları farklı çalışma prensiplerine sahiptir:
| Algoritma | Temel fikir |
|---|---|
| Kabarcık (Bubble Sort) | Komşu elemanları karşılaştırır, yanlış sıradaysa yer değiştirir. |
| Eklemeli (Insertion Sort) | Sıradaki elemanı sıralı kısımda doğru konuma yerleştirir. |
| Seçmeli (Selection Sort) | En küçük elemanı bulup başa yerleştirir, sonra kalanları sıralar. |
| Kabuk (Shell Sort) | Uzak elemanlarla başlayan, aralığı küçülen eklemeli sıralama. |
| Birleştirmeli (Merge Sort) | Diziyi yarıya böler, sıralar, sonra birleştirir. |
| Hızlı (Quick Sort) | Pivot etrafında parçalar, iki parçayı özyinelemeli sıralar. |
| Yığın (Heap Sort) | Max-heap kurar, en büyüğü tekrar tekrar sona taşır. |
| Saymalı / Basamak (Counting / Radix) | Karşılaştırma yapmaz; anahtarları sayar ya da basamak basamak dağıtır. |
| Timsort | Eklemeli + birleştirmeli karması; Java ve Python'un nesne sıralaması. |
Giriş
Bir Sıralama Algoritmasını Değerlendirmek
Giriş
Kararlılık (Stability): Örnek
Kayıtlar yalnızca sayıya (anahtar) göre sıralanır; harf, kaydın kimliğidir. Kararlı algoritmada anahtarı 3 olanlar girdideki gibi A, C, F sırasında kalır; seçmeli sıralamanın uzun mesafeli takasları bu sırayı C, A, F yapar. (Bu girdi, simülatör laboratuvarındaki örnektir.)
Kabarcık Sıralama
Kabarcık Sıralama (Bubble Sort)
- Her turda dizideki büyük elemanlar dizinin sonuna doğru kaydırılır; su içindeki kabarcık gibi yükselir.
- Komşu elemanlar ikili olarak karşılaştırılır.
- Sol eleman büyükse yerleri değiştirilir (swap).
- i. tur bittiğinde en büyük i eleman son yerlerindedir; bir sonraki tur bir eleman kısa olur.
- Bir turda hiç yer değiştirme olmazsa dizi sıralıdır → erken çıkış.
Kabarcık Sıralama · Adım adım
Kabarcık Sıralama Adım Adım: [3, 1, 5, 2, 6, 4]
Dizi: [3, 1, 5, 2, 6, 4]. Her turda komşu çiftler soldan sağa karşılaştırılır; büyük olan sağa kayar.
1. tur, j = 0: 3 > 1? Evet → yer değiştirilecek.
Yer değiştirme (takas): dizi artık [1, 3, 5, 2, 6, 4].
1. tur, j = 1: 3 > 5? Hayır → yerinde kalır.
1. tur, j = 2: 5 > 2? Evet → yer değiştirilecek.
Yer değiştirme (takas): dizi artık [1, 3, 2, 5, 6, 4].
1. tur, j = 3: 5 > 6? Hayır → yerinde kalır.
1. tur, j = 4: 6 > 4? Evet → yer değiştirilecek.
Yer değiştirme (takas): dizi artık [1, 3, 2, 5, 4, 6].
1. tur bitti: en büyük kalan eleman 6 son yerine (indis 5) yerleşti.
2. tur, j = 0: 1 > 3? Hayır → yerinde kalır.
2. tur, j = 1: 3 > 2? Evet → yer değiştirilecek.
Yer değiştirme (takas): dizi artık [1, 2, 3, 5, 4, 6].
2. tur, j = 2: 3 > 5? Hayır → yerinde kalır.
2. tur, j = 3: 5 > 4? Evet → yer değiştirilecek.
Yer değiştirme (takas): dizi artık [1, 2, 3, 4, 5, 6].
2. tur bitti: en büyük kalan eleman 5 son yerine (indis 4) yerleşti.
3. tur, j = 0: 1 > 2? Hayır → yerinde kalır.
3. tur, j = 1: 2 > 3? Hayır → yerinde kalır.
3. tur, j = 2: 3 > 4? Hayır → yerinde kalır.
3. turda hiç yer değiştirme olmadı → dizi sıralı; algoritma erken durur. Sonuç: [1, 2, 3, 4, 5, 6].
Kabarcık Sıralama
Kabarcık Sıralama: Java Kodu
boolean yerDegistiMi; for(int i = 0; i < n - 1; i++) { yerDegistiMi = false; for(int j = 0; j < n - 1 - i; j++) { if(dizi[j] > dizi[j+1]) { int gecici = dizi[j]; dizi[j] = dizi[j+1]; dizi[j+1] = gecici; yerDegistiMi = true; } } if(yerDegistiMi == false) break; }
- Dış döngü (i): en fazla n − 1 tur.
- İç döngü (j): sıralanmamış kısımdaki komşu çiftler; sondaki i eleman zaten yerinde olduğu için
j < n − 1 − i. - Yer değiştirme
gecicideğişkeniyle üç atamada yapılır. yerDegistiMibayrağı: tur boyunca hiç değiştirme yoksa dizi sıralıdır →break.
> olduğu (≥ değil) için eşit elemanlar asla yer değiştirmez: kabarcık sıralama kararlıdır.Kabarcık Sıralama · Kodu adım adım çalıştır
Kabarcık Sıralama: Kodu İzle, dizi = [5, 1, 9, 2, 10]
n = 5. yerDegistiMi bayrağı tanımlanır.
Dış döngü i = 0: bayrak false yapılır.
j = 0: dizi[0] = 5 > dizi[1] = 1? Evet.
gecici = 5 ile yer değiştirilir; bayrak true.
j = 1: dizi[1] = 5 > dizi[2] = 9? Hayır.
j = 2: dizi[2] = 9 > dizi[3] = 2? Evet.
gecici = 9 ile yer değiştirilir; bayrak true.
j = 3: dizi[3] = 9 > dizi[4] = 10? Hayır.
Tur sonu: 10 yerine oturdu. Bayrak true → devam.
Dış döngü i = 1: bayrak false yapılır.
j = 0: dizi[0] = 1 > dizi[1] = 5? Hayır.
j = 1: dizi[1] = 5 > dizi[2] = 2? Evet.
gecici = 5 ile yer değiştirilir; bayrak true.
j = 2: dizi[2] = 5 > dizi[3] = 9? Hayır.
Tur sonu: 9 yerine oturdu. Bayrak true → devam.
Dış döngü i = 2: bayrak false yapılır.
j = 0: dizi[0] = 1 > dizi[1] = 2? Hayır.
j = 1: dizi[1] = 2 > dizi[2] = 5? Hayır.
Bu turda yer değiştirme yok: break. Dizi sıralı: [1, 2, 5, 9, 10].
boolean yerDegistiMi; for(int i = 0; i < n - 1; i++) { yerDegistiMi = false; for(int j = 0; j < n - 1 - i; j++) { if(dizi[j] > dizi[j+1]) { int gecici = dizi[j]; dizi[j] = dizi[j+1]; dizi[j+1] = gecici; yerDegistiMi = true; } } if(yerDegistiMi == false) break; }
Kabarcık Sıralama
Kabarcık Sıralama: Analiz
| Durum | Girdi | Karşılaştırma | Yer değiştirme | Zaman |
|---|---|---|---|---|
| En iyi | sıralı | n − 1 (tek tur, bayrakla) | 0 | Θ(n) |
| Ortalama | rastgele | ≈ n²/2 | ≈ n²/4 | Θ(n²) |
| En kötü | ters sıralı | n(n − 1)/2 | n(n − 1)/2 | Θ(n²) |
- Anlaşılması ve yazılması çok kolay.
- Yerinde (O(1) ek bellek) ve kararlı.
- Bayrakla sıralı girdiyi Θ(n)'de tanır.
- Θ(n²): büyük dizilerde çok yavaş.
- Çok fazla yer değiştirme yapar (her ters çift = 1 takas).
- Pratikte eklemeli sıralamadan hep yavaştır.
Ters çift (inversion): i < j iken dizi[i] > dizi[j]. Kabarcık ve eklemeli sıralama her adımda tam olarak bir ters çifti düzeltir; ters sıralı dizide n(n − 1)/2 ters çift vardır.
Eklemeli Sıralama
Eklemeli Sıralama (Insertion Sort)
- Eldeki oyun kartlarını sıralamaya benzer: her yeni kartı elinizdeki sıralı kartların arasına yerleştirirsiniz.
- Dizi iki parçaya ayrılır: sıralı kısım (solda) ve sırasız kısım.
- Sırasız kısmın ilk elemanı alınır, sıralı kısımda doğru konumuna yerleştirilir.
- Bunun için ondan büyük elemanların bir sağa kaydırılması gerekir.
Eklemeli Sıralama · Adım adım
Eklemeli Sıralama Adım Adım: [3, 1, 5, 2, 6, 4]
Dizi [3, 1, 5, 2, 6, 4]. İlk eleman tek başına sıralı kısımdır; geri kalanı sırasız.
Sırasız kısmın ilk elemanı alınır: gecici = 1.
3 > 1 → 3 bir sağa kaydırılır (indis 0 → 1).
Dizinin başına gelindi → 1 indis 0'a yerleştirilir (1 kaydırma).
Sırasız kısmın ilk elemanı alınır: gecici = 5.
3 ≤ 5 → 5 indis 2'ye yerleştirilir (kaydırma gerekmedi).
Sırasız kısmın ilk elemanı alınır: gecici = 2.
5 > 2 → 5 bir sağa kaydırılır (indis 2 → 3).
3 > 2 → 3 bir sağa kaydırılır (indis 1 → 2).
1 ≤ 2 → 2 indis 1'e yerleştirilir (2 kaydırma).
Sırasız kısmın ilk elemanı alınır: gecici = 6.
5 ≤ 6 → 6 indis 4'e yerleştirilir (kaydırma gerekmedi).
Sırasız kısmın ilk elemanı alınır: gecici = 4.
6 > 4 → 6 bir sağa kaydırılır (indis 4 → 5).
5 > 4 → 5 bir sağa kaydırılır (indis 3 → 4).
3 ≤ 4 → 4 indis 3'e yerleştirilir (2 kaydırma). Dizi sıralı: [1, 2, 3, 4, 5, 6].
Eklemeli Sıralama
Eklemeli Sıralama: Java Kodu
for(int i = 1; i < n; i++) { int gecici = dizi[i]; int j = i - 1; while(j >= 0 && dizi[j] > gecici) { dizi[j+1] = dizi[j]; j = j - 1; } dizi[j+1] = gecici; }
- i = 1'den başlanır: tek elemanlı
dizi[0..0]zaten sıralıdır. gecici: yerleştirilecek eleman (anahtar). Kaydırmalar onun hücresinin üzerine yazacağı için önce saklanır.while: soldaki eleman büyük olduğu sürece onu bir sağa kaydır.- Döngü bitince boşalan yer
j + 1'dir.
Eklemeli Sıralama · Kodu adım adım çalıştır
Eklemeli Sıralama: Kodu İzle, dizi = [5, 1, 9, 2, 10]
n = 5. Sıralı kısım: yalnızca dizi[0].
i = 1: gecici = dizi[1] = 1, j = 0.
j = 0 ≥ 0 ve dizi[0] = 5 > 1 → döngüye gir.
dizi[1] = dizi[0] = 5 (sağa kaydır), j = -1.
j = −1: döngü biter.
dizi[0] = gecici = 1. Sıralı kısım: [1, 5].
i = 2: gecici = dizi[2] = 9, j = 1.
dizi[1] = 5 ≤ 9: döngü biter.
dizi[2] = gecici = 9. Sıralı kısım: [1, 5, 9].
i = 3: gecici = dizi[3] = 2, j = 2.
j = 2 ≥ 0 ve dizi[2] = 9 > 2 → döngüye gir.
dizi[3] = dizi[2] = 9 (sağa kaydır), j = 1.
j = 1 ≥ 0 ve dizi[1] = 5 > 2 → döngüye gir.
dizi[2] = dizi[1] = 5 (sağa kaydır), j = 0.
dizi[0] = 1 ≤ 2: döngü biter.
dizi[1] = gecici = 2. Sıralı kısım: [1, 2, 5, 9].
i = 4: gecici = dizi[4] = 10, j = 3.
dizi[3] = 9 ≤ 10: döngü biter.
dizi[4] = gecici = 10. Sıralı kısım: [1, 2, 5, 9, 10].
i = 5 < n değil → döngü biter. Sonuç: [1, 2, 5, 9, 10].
for(int i = 1; i < n; i++) { int gecici = dizi[i]; int j = i - 1; while(j >= 0 && dizi[j] > gecici) { dizi[j+1] = dizi[j]; j = j - 1; } dizi[j+1] = gecici; }
Eklemeli Sıralama
Eklemeli Sıralama: Analiz
| Durum | Girdi | Karşılaştırma | Kaydırma | Zaman |
|---|---|---|---|---|
| En iyi | sıralı | n − 1 | 0 | Θ(n) |
| Ortalama | rastgele | ≈ n²/4 | ≈ n²/4 | Θ(n²) |
| En kötü | ters sıralı | n(n − 1)/2 | n(n − 1)/2 | Θ(n²) |
- Kararlı, yerinde, çevrimiçi.
- Uyarlanabilir: süre Θ(n + ters çift sayısı).
- Küçük dizilerde (n ≲ 20–50) en hızlılardan.
- Rastgele büyük dizilerde Θ(n²).
- Bir eleman tek adımda yalnızca bir hücre ilerler; uzaktaki eleman çok kaydırma ister (→ kabuk sıralama).
Bu yüzden Timsort ve birçok kütüphane, hızlı/birleştirmeli sıralamada küçük alt dizileri eklemeli sıralamaya bırakır.
Seçmeli Sıralama
Seçmeli Sıralama (Selection Sort)
- Dizi iki bölüme ayrılır: sıralı ve sırasız.
- Her adımda:
- Sırasız bölümden en küçük eleman bulunur.
- Sırasız bölümün başındaki eleman ile yer değiştirilir.
- Yer değiştirmenin ardından sıralı bölümün parçası olur.
- Toplam n − 1 tur; her tur en fazla bir yer değiştirme.
Seçmeli Sıralama · Adım adım
Seçmeli Sıralama Adım Adım: [3, 1, 5, 2, 6, 4]
Dizi [3, 1, 5, 2, 6, 4], uzunluk = 6. Başta sıralı kısım boştur.
1. tur: sırasız kısmın ilk elemanı geçici en küçük kabul edilir: en küçük = 3.
1 < en küçük (3)? Evet → en küçük = 1.
5 < en küçük (1)? Hayır.
2 < en küçük (1)? Hayır.
6 < en küçük (1)? Hayır.
4 < en küçük (1)? Hayır.
En küçük (1) sırasız kısmın başıyla yer değiştirir → [1, 3, 5, 2, 6, 4]; sıralı kısım bir büyür.
2. tur: sırasız kısmın ilk elemanı geçici en küçük kabul edilir: en küçük = 3.
5 < en küçük (3)? Hayır.
2 < en küçük (3)? Evet → en küçük = 2.
6 < en küçük (2)? Hayır.
4 < en küçük (2)? Hayır.
En küçük (2) sırasız kısmın başıyla yer değiştirir → [1, 2, 5, 3, 6, 4]; sıralı kısım bir büyür.
3. tur: sırasız kısmın ilk elemanı geçici en küçük kabul edilir: en küçük = 5.
3 < en küçük (5)? Evet → en küçük = 3.
6 < en küçük (3)? Hayır.
4 < en küçük (3)? Hayır.
En küçük (3) sırasız kısmın başıyla yer değiştirir → [1, 2, 3, 5, 6, 4]; sıralı kısım bir büyür.
4. tur: sırasız kısmın ilk elemanı geçici en küçük kabul edilir: en küçük = 5.
6 < en küçük (5)? Hayır.
4 < en küçük (5)? Evet → en küçük = 4.
En küçük (4) sırasız kısmın başıyla yer değiştirir → [1, 2, 3, 4, 6, 5]; sıralı kısım bir büyür.
5. tur: sırasız kısmın ilk elemanı geçici en küçük kabul edilir: en küçük = 6.
5 < en küçük (6)? Evet → en küçük = 5.
En küçük (5) sırasız kısmın başıyla yer değiştirir → [1, 2, 3, 4, 5, 6]; sıralı kısım bir büyür.
5 tur sonunda son eleman kendiliğinden yerindedir. Sıralı dizi: [1, 2, 3, 4, 5, 6].
Seçmeli Sıralama
Seçmeli Sıralama: Java Kodu
public void sirala(int[] dizi) { int n = dizi.length; for(int i = 0; i < n - 1; i++) { int enKucuk = i; for(int j = i + 1; j < n; j++) { if(dizi[j] < dizi[enKucuk]) { enKucuk = j; } } int gecici = dizi[enKucuk]; dizi[enKucuk] = dizi[i]; dizi[i] = gecici; } }
enKucuken küçük elemanın indisini tutar (değerini değil): sonunda yer değiştirmek için indis gerekir.- İç döngü sırasız kısmın tamamını tarar; girdi sıralı olsa bile tarama kısalmaz.
- Tur başına bir yer değiştirme: toplam en fazla n − 1.
Seçmeli Sıralama · Kodu adım adım çalıştır
Seçmeli Sıralama: Kodu İzle, dizi = [5, 1, 10, 2, 9]
n = dizi.length = 5.
i = 0: enKucuk = 0 (dizi[enKucuk] = 5).
j = 1: dizi[1] = 1 < dizi[enKucuk] = 5? Evet.
enKucuk = 1 (yeni en küçük: 1).
j = 2: dizi[2] = 10 < dizi[enKucuk] = 1? Hayır.
j = 3: dizi[3] = 2 < dizi[enKucuk] = 1? Hayır.
j = 4: dizi[4] = 9 < dizi[enKucuk] = 1? Hayır.
j = 5 → iç döngü biter. gecici = 1; dizi[1] ↔ dizi[0] → [1, 5, 10, 2, 9].
i = 1: enKucuk = 1 (dizi[enKucuk] = 5).
j = 2: dizi[2] = 10 < dizi[enKucuk] = 5? Hayır.
j = 3: dizi[3] = 2 < dizi[enKucuk] = 5? Evet.
enKucuk = 3 (yeni en küçük: 2).
j = 4: dizi[4] = 9 < dizi[enKucuk] = 2? Hayır.
j = 5 → iç döngü biter. gecici = 2; dizi[3] ↔ dizi[1] → [1, 2, 10, 5, 9].
i = 2: enKucuk = 2 (dizi[enKucuk] = 10).
j = 3: dizi[3] = 5 < dizi[enKucuk] = 10? Evet.
enKucuk = 3 (yeni en küçük: 5).
j = 4: dizi[4] = 9 < dizi[enKucuk] = 5? Hayır.
j = 5 → iç döngü biter. gecici = 5; dizi[3] ↔ dizi[2] → [1, 2, 5, 10, 9].
i = 3: enKucuk = 3 (dizi[enKucuk] = 10).
j = 4: dizi[4] = 9 < dizi[enKucuk] = 10? Evet.
enKucuk = 4 (yeni en küçük: 9).
j = 5 → iç döngü biter. gecici = 9; dizi[4] ↔ dizi[3] → [1, 2, 5, 9, 10].
i = 4 < n − 1 değil → biter. Sonuç: [1, 2, 5, 9, 10].
public void sirala(int[] dizi) { int n = dizi.length; for(int i = 0; i < n - 1; i++) { int enKucuk = i; for(int j = i + 1; j < n; j++) { if(dizi[j] < dizi[enKucuk]) { enKucuk = j; } } int gecici = dizi[enKucuk]; dizi[enKucuk] = dizi[i]; dizi[i] = gecici; } }
Seçmeli Sıralama
Seçmeli Sıralama: Analiz
| Ölçü | Değer |
|---|---|
| Karşılaştırma | (n − 1) + (n − 2) + … + 1 = n(n − 1)/2, her girdide |
| Yer değiştirme | en fazla n − 1 |
| Zaman | en iyi = ortalama = en kötü = Θ(n²) |
| Ek bellek | O(1), yerinde |
| Kararlı mı? | Hayır (uzun mesafeli takas) |
En küçük (1), baştaki 2ₐ ile yer değiştirir: 2ₐ, 2ᵦ'nin arkasına geçer.
Uyarlanabilir değildir: sıralı bir diziye de n(n − 1)/2 karşılaştırma harcar.
Basit Algoritmalar · Karşılaştırma
Kabarcık, Eklemeli, Seçmeli: Aynı Girdide
Hücreler: karşılaştırma / yer değiştirme (ya da kaydırma) sayısı, n = 6. Değerler kodlar çalıştırılarak sayılmıştır.
| Algoritma | Taşıma türü | [3, 1, 5, 2, 6, 4] | sıralı 1…6 | ters 6…1 |
|---|---|---|---|---|
| Kabarcık (bayraklı) | takas | 12 / 5 | 5 / 0 | 15 / 15 |
| Eklemeli | kaydırma | 9 / 5 | 5 / 0 | 15 / 15 |
| Seçmeli | takas | 15 / 5 | 15 / 0 | 15 / 3 |
- Kabarcık ve eklemeli sıralamada taşıma sayısı = ters çift sayısı ([3, 1, 5, 2, 6, 4] için 5); bir takas 3, bir kaydırma 1 atamadır.
- Seçmeli sıralama girdiden bağımsız olarak hep n(n − 1)/2 = 15 karşılaştırma yapar ama en az yer değiştirmeyi o yapar.
- Sıralı girdide bayraklı kabarcık ve eklemeli yalnızca n − 1 = 5 karşılaştırma yapar (uyarlanabilir).
Kabuk Sıralama
Kabuk Sıralama (Shell Sort)
- D. Shell (1959): eklemeli sıralamanın genelleştirilmesi.
- Önce birbirinden aralık (gap) uzaklıktaki elemanlar eklemeli sıralanır; uzaktaki küçük elemanlar tek hamlede büyük mesafe atlar.
- Aralık küçültülür (n/2, n/4, …); son tur aralık = 1, yani sıradan eklemeli sıralama — ama dizi artık neredeyse sıralıdır.
- Yerinde, kararsız; süre aralık dizisine bağlı: n/2 aralıklarıyla en kötü Θ(n²), Hibbard (2ᵏ − 1) ile O(n1,5).
void kabukSirala(int[] dizi) { int n = dizi.length; for (int aralik = n / 2; aralik > 0; aralik /= 2) for (int i = aralik; i < n; i++) { int gecici = dizi[i], j; for (j = i; j >= aralik && dizi[j - aralik] > gecici; j -= aralik) dizi[j] = dizi[j - aralik]; dizi[j] = gecici; } }
Kabuk Sıralama · Adım adım
Kabuk Sıralama Adım Adım: [6, 5, 3, 1, 8, 7, 2, 4]
Dizi [6, 5, 3, 1, 8, 7, 2, 4], n = 8. Aralık dizisi: n/2 = 4, 2, … , 1.
aralik = 4: aralarında 4 indis olan elemanlar aynı grupta (aynı renk): [6, 8], [5, 7], [3, 2], [1, 4].
Her grup eklemeli sıralamayla sıralandı (1 kaydırma): [6, 8], [5, 7], [2, 3], [1, 4] → [6, 5, 2, 1, 8, 7, 3, 4].
aralik = 2: aralarında 2 indis olan elemanlar aynı grupta (aynı renk): [6, 2, 8, 3], [5, 1, 7, 4].
Her grup eklemeli sıralamayla sıralandı (6 kaydırma): [2, 3, 6, 8], [1, 4, 5, 7] → [2, 1, 3, 4, 6, 5, 8, 7].
aralik = 1: aralarında 1 indis olan elemanlar aynı grupta (aynı renk): [2, 1, 3, 4, 6, 5, 8, 7].
aralik = 1 sıradan eklemeli sıralamadır; dizi neredeyse sıralı olduğu için yalnızca 3 kaydırma gerekti. Sonuç: [1, 2, 3, 4, 5, 6, 7, 8].
Birleştirmeli Sıralama
Birleştirmeli Sıralama (Merge Sort)
- Böl ve fethet (divide and conquer) yaklaşımını kullanır.
- 1945'te John von Neumann tarafından geliştirilmiştir.
- En kötü durumda bile O(n log n).
- Kararlıdır; birleştirme için ek bellek (O(n)) gerekir.
1. Böl (divide): liste iki alt listeye bölünür; alt listeler tek eleman kalana kadar tekrar tekrar bölünür.
2. Fethet (conquer): her yarı aynı yöntemle (özyinelemeli) sıralanır.
3. Birleştir (combine): sıralı alt listeler, sıralı tek bir liste oluşana kadar birleştirilir.
Birleştirmeli Sıralama
Örnek: [38, 27, 43, 3, 9, 82, 10]
Yedi eleman: orta = ⌊(sol + sağ)/2⌋ olduğu için sol yarı bir eleman fazla alır ([38, 27, 43, 3] ve [9, 82, 10]).
Birleştirmeli Sıralama · Adım adım
Merge Sort Adım Adım: [6, 5, 3, 1, 8, 7, 2, 4]
Dizi [6, 5, 3, 1, 8, 7, 2, 4]: önce tek eleman kalana kadar bölünür, sonra sıralı parçalar birleştirilir.
Böl: [6, 5, 3, 1, 8, 7, 2, 4] → [6, 5, 3, 1] ve [8, 7, 2, 4].
Böl: [6, 5, 3, 1] → [6, 5] ve [3, 1].
Böl: [6, 5] → [6] ve [5]. Tek elemanlı parçalar zaten sıralıdır.
Birleştir: [6] + [5] → [5, 6].
Böl: [3, 1] → [3] ve [1]. Tek elemanlı parçalar zaten sıralıdır.
Birleştir: [3] + [1] → [1, 3].
Birleştir: [5, 6] + [1, 3] → [1, 3, 5, 6].
Böl: [8, 7, 2, 4] → [8, 7] ve [2, 4].
Böl: [8, 7] → [8] ve [7]. Tek elemanlı parçalar zaten sıralıdır.
Birleştir: [8] + [7] → [7, 8].
Böl: [2, 4] → [2] ve [4]. Tek elemanlı parçalar zaten sıralıdır.
Birleştir: [2] + [4] → [2, 4].
Birleştir: [7, 8] + [2, 4] → [2, 4, 7, 8].
Birleştir: [1, 3, 5, 6] + [2, 4, 7, 8] → [1, 2, 3, 4, 5, 6, 7, 8]. Dizi sıralandı.
Birleştirmeli Sıralama
Merge Sort: Java Kodu
void birlestirmeliSirala(int[] dizi, int sol, int sag) { if (sol < sag) { int orta = sol + (sag - sol) / 2; birlestirmeliSirala(dizi, sol, orta); birlestirmeliSirala(dizi, orta + 1, sag); birlestir(dizi, sol, orta, sag); } }
void birlestir(int[] A, int p, int q, int r) { int[] L = Arrays.copyOfRange(A, p, q + 1); int[] R = Arrays.copyOfRange(A, q + 1, r + 1); int i = 0, j = 0, k = p; while (i < L.length && j < R.length) { if (L[i] <= R[j]) A[k++] = L[i++]; else A[k++] = R[j++]; } while (i < L.length) A[k++] = L[i++]; while (j < R.length) A[k++] = R[j++]; }
sol + (sag − sol) / 2 yazımı, (sol + sag) / 2 toplamındaki int taşmasını önler.
Birleştirmeli Sıralama · Kodu adım adım çalıştır
birlestir(): İki Sıralı Diziyi Birleştir
L = A[p..q] = [1, 3, 5, 6] ve R = A[q+1..r] = [2, 4, 7, 8] kopyalanır (ikisi de sıralı). i = j = 0, k = p = 0.
L[0] = 1 ≤ R[0] = 2 → A[0] = 1; i++, k++.
L[1] = 3 > R[0] = 2 → A[1] = 2; j++, k++.
L[1] = 3 ≤ R[1] = 4 → A[2] = 3; i++, k++.
L[2] = 5 > R[1] = 4 → A[3] = 4; j++, k++.
L[2] = 5 ≤ R[2] = 7 → A[4] = 5; i++, k++.
L[3] = 6 ≤ R[2] = 7 → A[5] = 6; i++, k++.
L bitti (i = 4); kalan R elemanları sırayla kopyalanır: A[6] = 7.
A[7] = 8. Birleştirme bitti: [1, 2, 3, 4, 5, 6, 7, 8] (8 eleman, en fazla 7 karşılaştırma).
void birlestir(int[] A, int p, int q, int r) { int[] L = Arrays.copyOfRange(A, p, q + 1); int[] R = Arrays.copyOfRange(A, q + 1, r + 1); int i = 0, j = 0, k = p; while (i < L.length && j < R.length) { if (L[i] <= R[j]) A[k++] = L[i++]; else A[k++] = R[j++]; } while (i < L.length) A[k++] = L[i++]; while (j < R.length) A[k++] = R[j++]; }
Birleştirmeli Sıralama
Merge Sort: Analiz
Yineleme bağıntısı (recurrence):
T(n) = 2 T(n/2) + Θ(n)
- Her düzeyde toplam birleştirme işi Θ(n).
- Düzey sayısı log₂ n (n'yi 1'e kadar yarılamak).
- Toplam: Θ(n log n) — en iyi = ortalama = en kötü.
- Ek bellek Θ(n) (yardımcı diziler) + Θ(log n) özyineleme yığını.
L[i] <= R[j]: eşitlikte soldan alınır → kararlı.
Hızlı Sıralama
Hızlı Sıralama (Quick Sort)
- C. A. R. Hoare (1959). Yine böl ve fethet, ama işin çoğu bölmede yapılır.
- Bir pivot eleman seçilir.
- Dizi pivot etrafında iki parçaya bölünür (parçalama, partitioning): solda ≤ x, sağda ≥ x.
- İki alt parça özyinelemeli olarak sıralanır. Birleştirme adımına gerek yoktur.
Hızlı Sıralama
Parçalama (Partitioning) Prosedürü
Lomuto: pivot = son eleman
parcala(A, p, q): pivot = A[q] // pivot seç i = p - 1 // ≤ pivot bölgesinin sonu for j = p to q - 1: if A[j] <= pivot: i = i + 1 takas(A[i], A[j]) takas(A[i + 1], A[q]) // pivotu yerleştir return i + 1 // pivotun indisi
Aynı fikir: pivot = ilk eleman
PARCALA(A, p, q):
x = A[p] // pivot
i = p
for j = p + 1 to q:
if A[j] <= x:
i = i + 1
takas(A[i], A[j])
takas(A[p], A[i])
return iDeğişmez (invariant): A[p..i] ≤ pivot, A[i+1..j−1] > pivot, A[j..] henüz incelenmedi. Tek geçiş: Θ(n) zaman, O(1) ek bellek. Sonraki örnek sağdaki sürümü, kod izleme soldakini kullanır.
Hızlı Sıralama · Adım adım
Parçalara Ayırma: [6, 10, 13, 5, 8, 3, 2, 11]
Pivot x = A[p] = 6 (sarı). i = p; j, p + 1'den başlar. Mavi: ≤ x, kırmızı: > x, beyaz: henüz bakılmadı.
A[j] = 10 > 6 → yalnızca j ilerler; kırmızı bölge büyür.
A[j] = 13 > 6 → yalnızca j ilerler; kırmızı bölge büyür.
A[j] = 5 ≤ 6 → i = 1; A[i] ↔ A[j]: [6, 5, 13, 10, 8, 3, 2, 11]. Mavi bölge bir büyüdü.
A[j] = 8 > 6 → yalnızca j ilerler; kırmızı bölge büyür.
A[j] = 3 ≤ 6 → i = 2; A[i] ↔ A[j]: [6, 5, 3, 10, 8, 13, 2, 11]. Mavi bölge bir büyüdü.
A[j] = 2 ≤ 6 → i = 3; A[i] ↔ A[j]: [6, 5, 3, 2, 8, 13, 10, 11]. Mavi bölge bir büyüdü.
A[j] = 11 > 6 → yalnızca j ilerler; kırmızı bölge büyür.
Döngü bitti. Pivot, mavi bölgenin son elemanıyla yer değiştirir: [2, 5, 3, 6, 8, 13, 10, 11]. Pivot 6 artık kesin yerinde (indis 3); solu ≤ 6, sağı ≥ 6.
Hızlı Sıralama · Kodu adım adım çalıştır
parcala(): Kodu İzle, dizi = [2, 8, 7, 1, 3, 5, 6, 4]
pivot = dizi[ust] = 4; i = alt − 1 = −1 (≤ pivot bölgesi boş).
j = 0: dizi[0] = 2 ≤ 4? Evet.
i = 0; takas(dizi, 0, 0) → [2, 8, 7, 1, 3, 5, 6, 4] (kendisiyle).
j = 1: dizi[1] = 8 ≤ 4? Hayır → j ilerler.
j = 2: dizi[2] = 7 ≤ 4? Hayır → j ilerler.
j = 3: dizi[3] = 1 ≤ 4? Evet.
i = 1; takas(dizi, 1, 3) → [2, 1, 7, 8, 3, 5, 6, 4].
j = 4: dizi[4] = 3 ≤ 4? Evet.
i = 2; takas(dizi, 2, 4) → [2, 1, 3, 8, 7, 5, 6, 4].
j = 5: dizi[5] = 5 ≤ 4? Hayır → j ilerler.
j = 6: dizi[6] = 6 ≤ 4? Hayır → j ilerler.
Döngü bitti. takas(dizi, i+1 = 3, ust = 7) → [2, 1, 3, 4, 7, 5, 6, 8].
return 3: pivot 4 kesin yerinde; sol [0..2] ≤ 4, sağ [4..7] > 4.
int parcala(int[] dizi, int alt, int ust) { int pivot = dizi[ust]; int i = alt - 1; for (int j = alt; j < ust; j++) { if (dizi[j] <= pivot) { i++; takas(dizi, i, j); } } takas(dizi, i + 1, ust); return i + 1; }
Hızlı Sıralama
QuickSort: Özyinelemeli Yapı
HIZLI-SIRALA(A, p, r)
if p < r
q = PARCALA(A, p, r)
HIZLI-SIRALA(A, p, q − 1)
HIZLI-SIRALA(A, q + 1, r)
İlk çağrı: HIZLI-SIRALA(A, 1, n)void hizliSirala(int[] dizi, int alt, int ust) { if (alt < ust) { int p = parcala(dizi, alt, ust); hizliSirala(dizi, alt, p - 1); hizliSirala(dizi, p + 1, ust); } }
- Pivot (indis q) iki özyinelemeli çağrının hiçbirine dahil edilmez: yerine oturmuştur.
- Taban durumu: 0 ya da 1 elemanlı alt dizi (p ≥ r) zaten sıralıdır.
- Java sürümü 0-tabanlıdır: ilk çağrı
hizliSirala(dizi, 0, n − 1).
Hızlı Sıralama · Adım adım
QuickSort Adım Adım: [6, 10, 13, 5, 8, 3, 2, 11]
Dizi [6, 10, 13, 5, 8, 3, 2, 11]. Her çağrı alt diziyi pivot = ilk eleman ile parçalar, sonra iki tarafı özyinelemeli sıralar.
Derinlik 0: [6, 10, 13, 5, 8, 3, 2, 11] pivot 6 ile parçalanır → pivot indis 3'e oturur; sonra [0..2] ve [4..7] sıralanır.
Derinlik 1: [2, 5, 3] pivot 2 ile parçalanır → pivot indis 0'a oturur; sonra [0..-1] ve [1..2] sıralanır.
Derinlik 2: [5, 3] pivot 5 ile parçalanır → pivot indis 2'ye oturur; sonra [1..1] ve [3..2] sıralanır.
Derinlik 3: [1..1] tek elemanlı (3) → zaten sıralı (p < r değil).
Derinlik 1: [8, 13, 10, 11] pivot 8 ile parçalanır → pivot indis 4'e oturur; sonra [4..3] ve [5..7] sıralanır.
Derinlik 2: [13, 10, 11] pivot 13 ile parçalanır → pivot indis 7'ye oturur; sonra [5..6] ve [8..7] sıralanır.
Derinlik 3: [11, 10] pivot 11 ile parçalanır → pivot indis 6'ya oturur; sonra [5..5] ve [7..6] sıralanır.
Derinlik 4: [5..5] tek elemanlı (10) → zaten sıralı (p < r değil).
Tüm çağrılar bitti; birleştirme adımı yok, dizi yerinde sıralandı: [2, 3, 5, 6, 8, 10, 11, 13].
Hızlı Sıralama
QuickSort: Analiz ve Pivot Seçimi
| Durum | Ne zaman? | Yineleme | Zaman |
|---|---|---|---|
| En iyi | pivot her seferinde ortanca | T(n) = 2T(n/2) + Θ(n) | Θ(n log n) |
| Ortalama | rastgele girdi / rastgele pivot | beklenen ≈ 1,39 n log₂ n karşılaştırma | Θ(n log n) |
| En kötü | pivot hep en küçük/en büyük (ör. sıralı dizi + son eleman pivot) | T(n) = T(n − 1) + Θ(n) | Θ(n²) |
Yerinde (O(log n) ortalama yığın), kararsız. Önbellek dostu olduğu için pratikte genellikle en hızlı karşılaştırmalı sıralamadır (Java Arrays.sort(int[]): çift pivotlu quicksort).
Yığın Sıralama
Yığın Sıralama (Heap Sort)
- Yığın (heap) veri yapısını kullanarak sıralar; ortalama ve en kötü durumda O(n log n).
- Max-heap: her düğüm çocuklarından büyük ya da eşittir → en büyük eleman köktedir.
- Ağaç ayrı bir yapı değildir; dizinin kendisidir: dizi[i]'nin çocukları
2i + 1ve2i + 2, ebeveyni(i − 1) / 2.
- Dizi max-heap yapısına dönüştürülür.
- En büyük eleman (kök) dizinin sonuna yerleştirilir.
- Kalan dizi tekrar max-heap yapılır (kök aşağı süzülür).
- Tüm elemanlar sıralanana kadar tekrarlanır.
Yığın Sıralama
Örnek: [4, 10, 3, 5, 1]
| Adım | Dizi (yığın | sıralı) | Açıklama |
|---|---|---|
| Dizi | [4, 10, 3, 5, 1] | |
| Max-heap oluştur | [10, 5, 3, 4, 1] | kök = en büyük |
| Kök ↔ dizi[4] | [1, 5, 3, 4] | [10] | 10 sıralı bölgeye |
| Yeniden yığınlaştır | [5, 4, 3, 1] | [10] | kök aşağı süzüldü |
| Kök ↔ dizi[3] | [1, 4, 3] | [5, 10] | 5 sıralı bölgeye |
| Yeniden yığınlaştır | [4, 1, 3] | [5, 10] | kök aşağı süzüldü |
| Kök ↔ dizi[2] | [3, 1] | [4, 5, 10] | 4 sıralı bölgeye |
| Yeniden yığınlaştır | [3, 1] | [4, 5, 10] | kök aşağı süzüldü |
| Kök ↔ dizi[1] | [1] | [3, 4, 5, 10] | 3 sıralı bölgeye |
| Sonuç | [1, 3, 4, 5, 10] |
Yığın Sıralama · Adım adım
1. Aşama: Max-Heap Kurma — [6, 5, 3, 1, 8, 7, 2, 4]
Dizi [6, 5, 3, 1, 8, 7, 2, 4] tam ikili ağaç olarak düşünülür: dizi[i]'nin çocukları 2i+1 ve 2i+2. Henüz yığın (heap) değil.
yiginlastir(i = 3): 1 çocuklarından küçük → yer değiştir (1 ↔ 4). Dizi: [6, 5, 3, 4, 8, 7, 2, 1].
yiginlastir(i = 2): 3 çocuklarından küçük → yer değiştir (3 ↔ 7). Dizi: [6, 5, 7, 4, 8, 3, 2, 1].
yiginlastir(i = 1): 5 çocuklarından küçük → yer değiştir (5 ↔ 8). Dizi: [6, 8, 7, 4, 5, 3, 2, 1].
yiginlastir(i = 0): 6 çocuklarından küçük → yer değiştir (6 ↔ 8). Dizi: [8, 6, 7, 4, 5, 3, 2, 1].
Yaprak olmayan tüm düğümler (i = 3 … 0) işlendi: max-heap [8, 6, 7, 4, 5, 3, 2, 1]. En büyük eleman (8) kökte.
Yığın Sıralama · Adım adım
2. Aşama: Kökü Sona Taşı, Süz
Max-heap: [8, 6, 7, 4, 5, 3, 2, 1]. Her turda kök (en büyük) yığının son elemanıyla yer değiştirir ve yığın bir küçülür.
Kök 8 ↔ dizi[7] = 1: 8 sıralı bölgeye (yeşil) geçti. Yığın boyutu 7.
yiginlastir(0) ile kök aşağı süzülür (1 ↔ 7; 1 ↔ 3) → yığın: [7, 6, 3, 4, 5, 1, 2].
Kök 7 ↔ dizi[6] = 2: 7 sıralı bölgeye (yeşil) geçti. Yığın boyutu 6.
yiginlastir(0) ile kök aşağı süzülür (2 ↔ 6; 2 ↔ 5) → yığın: [6, 5, 3, 4, 2, 1].
Kök 6 ↔ dizi[5] = 1: 6 sıralı bölgeye (yeşil) geçti. Yığın boyutu 5.
yiginlastir(0) ile kök aşağı süzülür (1 ↔ 5; 1 ↔ 4) → yığın: [5, 4, 3, 1, 2].
Kök 5 ↔ dizi[4] = 2: 5 sıralı bölgeye (yeşil) geçti. Yığın boyutu 4.
yiginlastir(0) ile kök aşağı süzülür (2 ↔ 4) → yığın: [4, 2, 3, 1].
Kök 4 ↔ dizi[3] = 1: 4 sıralı bölgeye (yeşil) geçti. Yığın boyutu 3.
yiginlastir(0) ile kök aşağı süzülür (1 ↔ 3) → yığın: [3, 2, 1].
Kök 3 ↔ dizi[2] = 1: 3 sıralı bölgeye (yeşil) geçti. Yığın boyutu 2.
yiginlastir(0) ile kök aşağı süzülür (1 ↔ 2) → yığın: [2, 1].
Kök 2 ↔ dizi[1] = 1: 2 sıralı bölgeye (yeşil) geçti. Yığın boyutu 1.
Yığında tek eleman kaldı; o da yerinde. Sıralı dizi: [1, 2, 3, 4, 5, 6, 7, 8].
Yığın Sıralama
Heap Sort: Java Kodu ve Analiz
void yiginSirala(int[] dizi) { int n = dizi.length; for (int i = n / 2 - 1; i >= 0; i--) // 1) yığın kur yiginlastir(dizi, n, i); for (int i = n - 1; i > 0; i--) { // 2) sırala takas(dizi, 0, i); // en büyüğü sona yiginlastir(dizi, i, 0); // kökü süz } } void yiginlastir(int[] dizi, int n, int i) { int enBuyuk = i, sol = 2 * i + 1, sag = 2 * i + 2; if (sol < n && dizi[sol] > dizi[enBuyuk]) enBuyuk = sol; if (sag < n && dizi[sag] > dizi[enBuyuk]) enBuyuk = sag; if (enBuyuk != i) { takas(dizi, i, enBuyuk); yiginlastir(dizi, n, enBuyuk); } }
- Yığın kurma: yapraklar (i ≥ n/2) zaten tek elemanlı yığındır; i = n/2 − 1'den köke doğru süzülür. Toplam O(n) (çoğu düğüm alçaktadır).
- Sıralama: n − 1 kez kök sona alınır ve O(log n)'de süzülür → O(n log n).
- En kötü durumda bile O(n log n), O(1) ek bellek (yerinde).
- Kararsız: kök–son takası eşit anahtarların sırasını bozabilir.
Yığın, elemanları tek tek ekleyerek (yukarı süzme) de kurulabilir; bu da geçerli bir max-heap verir ama O(n log n) sürer. Buradaki aşağıdan yukarı kurma (Floyd yöntemi) O(n)'dir.
Doğrusal Zamanlı Sıralama
Karşılaştırmalı Sıralamanın Alt Sınırı
- Yalnızca "ai ≤ aj?" sorusu soran her algoritma bir karar ağacı (decision tree) ile gösterilebilir.
- Her yaprak bir sıralamadır (permütasyon); doğru çalışmak için n! yaprak gerekir.
- Yüksekliği h olan ikili ağacın en fazla 2h yaprağı vardır: 2h ≥ n! ⇒ h ≥ log₂(n!) = Ω(n log n).
- Sonuç: birleştirmeli ve yığın sıralama asimptotik olarak en iyidir.
- Daha hızlı olmak için karşılaştırma dışında bilgi gerekir: anahtarlar küçük tamsayılar → saymalı, basamak.
Doğrusal Zamanlı Sıralama
Saymalı Sıralama (Counting Sort)
- Elemanlar birbiriyle karşılaştırılmaz.
- Girdi: A[1 … n], A[j] ∈ {1, 2, …, k}.
- Çıktı: B[1 … n], sıralı.
- Geçici: C[1 … k] sayaçları.
Fikir: anahtarı ≤ i olan eleman sayısı biliniyorsa, i anahtarlı son eleman tam o konuma yazılır.
for i = 1 to k C[i] = 0 for j = 1 to n C[A[j]] = C[A[j]] + 1 // C[i] = |{anahtar = i}| for i = 2 to k C[i] = C[i] + C[i−1] // C[i] = |{anahtar ≤ i}| for j = n downto 1 B[C[A[j]]] = A[j] C[A[j]] = C[A[j]] − 1
Doğrusal Zamanlı Sıralama · Adım adım
Saymalı Sıralama Adım Adım: A = [4, 1, 3, 4, 3]
Girdi A = [4, 1, 3, 4, 3], n = 5, anahtarlar {1, …, 4}. C: sayaç dizisi (k = 4), B: çıktı.
Döngü 1: for i = 1 to k: C[i] = 0 — tüm sayaçlar sıfırlanır. Θ(k).
Döngü 2, j = 1: A[1] = 4 → C[4]++ = 1. (C[i] = i anahtarının adedi)
Döngü 2, j = 2: A[2] = 1 → C[1]++ = 1. (C[i] = i anahtarının adedi)
Döngü 2, j = 3: A[3] = 3 → C[3]++ = 1. (C[i] = i anahtarının adedi)
Döngü 2, j = 4: A[4] = 4 → C[4]++ = 2. (C[i] = i anahtarının adedi)
Döngü 2, j = 5: A[5] = 3 → C[3]++ = 2. (C[i] = i anahtarının adedi)
Döngü 3, i = 2: C[2] = C[2] + C[1] = 1. (C[i] = anahtarı ≤ i olanların adedi)
Döngü 3, i = 3: C[3] = C[3] + C[2] = 3. (C[i] = anahtarı ≤ i olanların adedi)
Döngü 3, i = 4: C[4] = C[4] + C[3] = 5. (C[i] = anahtarı ≤ i olanların adedi)
Önek toplamı hazır: C = [1, 1, 3, 5]. C[i], i anahtarlı son elemanın B'deki konumudur.
Döngü 4, j = 5: A[5] = 3 → B[C[3] = 3] = 3; C[3]−− = 2.
Döngü 4, j = 4: A[4] = 4 → B[C[4] = 5] = 4; C[4]−− = 4.
Döngü 4, j = 3: A[3] = 3 → B[C[3] = 2] = 3; C[3]−− = 1.
Döngü 4, j = 2: A[2] = 1 → B[C[1] = 1] = 1; C[1]−− = 0.
Döngü 4, j = 1: A[1] = 4 → B[C[4] = 4] = 4; C[4]−− = 3.
Sonuç B = [1, 3, 3, 4, 4]. Hiç karşılaştırma yapılmadı; sondan başa gezildiği için eşit anahtarların sırası korunur (kararlı).
Doğrusal Zamanlı Sıralama
Saymalı Sıralama: Karmaşıklık ve Java
| Döngü | İş | Süre |
|---|---|---|
| 1 | C[1..k] = 0 | Θ(k) |
| 2 | sayma | Θ(n) |
| 3 | önek toplamı | Θ(k) |
| 4 | B'ye yerleştir | Θ(n) |
| Toplam | Θ(n + k) | |
int[] saymaliSirala(int[] dizi) { int maks = enBuyuk(dizi); int[] sayac = new int[maks + 1]; for (int x : dizi) sayac[x]++; for (int i = 1; i <= maks; i++) sayac[i] += sayac[i - 1]; int[] cikti = new int[dizi.length]; for (int i = dizi.length - 1; i >= 0; i--) { cikti[sayac[dizi[i]] - 1] = dizi[i]; sayac[dizi[i]]--; } return cikti; }
- k = O(n) ise Θ(n): Ω(n log n) sınırını aşar, çünkü karşılaştırma yapmaz.
- Ek bellek Θ(n + k); k çok büyükse (ör. 32 bitlik tamsayılar) pratik değildir. Java sürümü 0-tabanlıdır ve 0 … maks aralığını kullanır.
- Son döngünün sondan başa gezmesi kararlılığı sağlar → basamak sıralamanın temeli.
Doğrusal Zamanlı Sıralama
Basamak Sıralama (Radix Sort)
- Tamsayıları basamak basamak sıralar; en anlamsız basamaktan (birler) başlar: LSD.
- Her geçişte o basamağa göre kararlı bir sıralama (saymalı, k = 10) yapılır.
- Kararlılık şarttır: onlar basamağına göre sıralarken birler sırası korunmalıdır.
- d basamak, taban b: Θ(d · (n + b)). d sabitse Θ(n).
- Ek bellek Θ(n + b); yerinde değildir.
int[] basamakSirala(int[] dizi) { int maks = enBuyuk(dizi); for (int us = 1; maks / us > 0; us *= 10) dizi = basamagaGoreSay(dizi, us); return dizi; } int[] basamagaGoreSay(int[] dizi, int us) { int n = dizi.length; int[] cikti = new int[n], sayac = new int[10]; for (int x : dizi) sayac[(x / us) % 10]++; for (int i = 1; i < 10; i++) sayac[i] += sayac[i - 1]; for (int i = n - 1; i >= 0; i--) { int b = (dizi[i] / us) % 10; cikti[--sayac[b]] = dizi[i]; } return cikti; }
Doğrusal Zamanlı Sıralama · Adım adım
Basamak Sıralama Adım Adım
Girdi [170, 45, 75, 90, 802, 24, 2, 66]. En büyük sayı 802: 3 basamak → 3 geçiş (en anlamsız basamaktan başlayarak, LSD).
Geçiş 1: her sayının birler basamağı (altta) anahtar olarak kullanılır; eksik basamak 0 sayılır.
Kararlı saymalı sıralama sonrası: [170, 90, 802, 2, 24, 45, 75, 66]. Eşit basamaklılar önceki sıralarını korudu.
Geçiş 2: her sayının onlar basamağı (altta) anahtar olarak kullanılır; eksik basamak 0 sayılır.
Kararlı saymalı sıralama sonrası: [802, 2, 24, 45, 66, 170, 75, 90]. Eşit basamaklılar önceki sıralarını korudu.
Geçiş 3: her sayının yüzler basamağı (altta) anahtar olarak kullanılır; eksik basamak 0 sayılır.
Kararlı saymalı sıralama sonrası: [2, 24, 45, 66, 75, 90, 170, 802]. Tüm basamaklar işlendi → sıralı.
Doğrusal Zamanlı Sıralama
Kova Sıralama (Bucket Sort)
- Girdi [0, 1) aralığına düzgün dağılmış sayılar olsun.
- n kova açılır; x değeri
⌊n · x⌋numaralı kovaya atılır. - Her kova (eklemeli sıralamayla) sıralanır, kovalar sırayla birleştirilir.
- Beklenen süre Θ(n); tüm elemanlar tek kovaya düşerse en kötü Θ(n²).
- Simülatördeki onuncu algoritmadır; dağılım hakkında varsayım yapar.
Timsort
Timsort: Hibrit Sıralama
- Tim Peters (2002, Python). Java'da nesne dizileri (
Arrays.sort(Object[]),List.sort) ve Python'dasortedTimsort kullanır. - Gerçek veride sık görülen sıralı parçaları (run) kullanır: kısa run'lar eklemeli sıralamayla uzatılır, run'lar birleştirmeli sıralamayla birleştirilir.
- Kararlı; en kötü O(n log n), sıralı girdide Θ(n); O(n) ek bellek.
static final int MIN_PARCA = 4; void timSirala(int[] dizi) { int n = dizi.length; for (int i = 0; i < n; i += MIN_PARCA) // 1) run'lar eklemeliSirala(dizi, i, Math.min(i + MIN_PARCA - 1, n - 1)); for (int boyut = MIN_PARCA; boyut < n; boyut *= 2) // 2) birleştir for (int sol = 0; sol < n; sol += 2 * boyut) { int orta = sol + boyut - 1; int sag = Math.min(sol + 2 * boyut - 1, n - 1); if (orta < sag) birlestir(dizi, sol, orta, sag); } }
Buradaki sürüm sabit uzunluklu run'larla basitleştirilmiştir (gerçek Timsort'ta minrun 32–64, doğal run tespiti ve "galloping" vardır).
Timsort · Adım adım
Timsort Adım Adım (MIN_PARCA = 4)
Dizi [5, 21, 7, 23, 19, 10, 3, 12, 8, 1], n = 10. Dizi 4'erli parçalara (run) ayrılır (renkler).
Run [0..3] eklemeli sıralama ile sıralandı: [5, 7, 21, 23].
Run [4..7] eklemeli sıralama ile sıralandı: [3, 10, 12, 19].
Run [8..9] eklemeli sıralama ile sıralandı: [1, 8].
boyut = 4: [5, 7, 21, 23] + [3, 10, 12, 19] birleştirildi → [3, 5, 7, 10, 12, 19, 21, 23].
boyut = 4: [8..9] parçasının birleşeceği eşi yok (orta = 11 ≥ sag = 9) → aynen kalır.
boyut = 8: [3, 5, 7, 10, 12, 19, 21, 23] + [1, 8] birleştirildi → [1, 3, 5, 7, 8, 10, 12, 19, 21, 23]. Dizi sıralandı.
Karşılaştırma
Sıralama Algoritmalarının Karşılaştırılması
| Algoritma | En iyi | Ortalama | En kötü | Ek bellek | Kararlı | Yerinde |
|---|---|---|---|---|---|---|
| Kabarcık (bayraklı) | Θ(n) | Θ(n²) | Θ(n²) | O(1) | Evet | Evet |
| Eklemeli | Θ(n) | Θ(n²) | Θ(n²) | O(1) | Evet | Evet |
| Seçmeli | Θ(n²) | Θ(n²) | Θ(n²) | O(1) | Hayır | Evet |
| Kabuk (n/2 aralık) | Θ(n log n) | aralığa bağlı | Θ(n²) | O(1) | Hayır | Evet |
| Birleştirmeli | Θ(n log n) | Θ(n log n) | Θ(n log n) | Θ(n) | Evet | Hayır |
| Hızlı | Θ(n log n) | Θ(n log n) | Θ(n²) | O(log n)* | Hayır | Evet |
| Yığın | O(n log n) | Θ(n log n) | Θ(n log n) | O(1) | Hayır | Evet |
| Saymalı | Θ(n + k) | Θ(n + k) | Θ(n + k) | Θ(n + k) | Evet | Hayır |
| Basamak (LSD) | Θ(d(n + b)) | Θ(d(n + b)) | Θ(d(n + b)) | Θ(n + b) | Evet | Hayır |
| Kova | Θ(n) | Θ(n)† | Θ(n²) | Θ(n) | Evet | Hayır |
| Timsort | Θ(n) | Θ(n log n) | Θ(n log n) | O(n) | Evet | Hayır |
* özyineleme yığını (küçük parçaya önce inilirse). † düzgün dağılımda beklenen. k: anahtar aralığı, d: basamak sayısı, b: taban. Timsort'un Θ(n) en iyi durumu doğal run tespitiyle gerçek sürüm içindir.
Karşılaştırma
Kaç Karşılaştırma? n = 1000
| Algoritma | Rastgele | Sıralı | Ters sıralı |
|---|---|---|---|
| Kabarcık (bayraklı) | 497.960 | 999 | 499.500 |
| Eklemeli | 252.205 | 999 | 499.500 |
| Seçmeli | 499.500 | 499.500 | 499.500 |
| Kabuk (n/2) | 14.723 | 8.006 | 11.716 |
| Birleştirmeli | 8.693 | 5.044 | 4.932 |
| Hızlı (son eleman pivot) | 11.384 | 499.500 | 499.500 |
| Yığın | 16.791 | 17.583 | 15.965 |
- Eleman karşılaştırma sayıları; bu bölümdeki kodlar n = 1000 için çalıştırılarak sayıldı (rastgele dizi sabit tohumla üretildi).
- Karşılaştırma için: n² / 2 ≈ 500.000, n log₂ n ≈ 9.966. Sıralı girdide son eleman pivotlu hızlı sıralama en kötü durumuna düşer.
Karşılaştırma
Hangi Durumda Hangi Algoritma?
Kabarcık ve seçmeli sıralama öğretim amaçlıdır; seçmeli yalnızca yazmanın çok pahalı olduğu durumlarda anlamlıdır.
Özet
Özet
| Konu | Hatırlanacaklar |
|---|---|
| Ölçütler | zaman (en iyi/ortalama/en kötü), ek bellek, kararlılık, yerinde, uyarlanabilirlik |
| Kabarcık | komşu takas; tur sonunda en büyük sonda; bayrakla en iyi Θ(n), aksi Θ(n²); kararlı |
| Eklemeli | anahtarı sakla, büyükleri kaydır, yerleştir; Θ(n + ters çift); küçük/neredeyse sıralı veride en iyi |
| Seçmeli | en küçüğü bul, başa al; her zaman n(n − 1)/2 karşılaştırma, ≤ n − 1 takas; kararsız |
| Kabuk | azalan aralıklarla eklemeli sıralama; yerinde, kararsız |
| Birleştirmeli | böl–sırala–birleştir; T(n) = 2T(n/2) + Θ(n) = Θ(n log n); Θ(n) ek bellek; kararlı |
| Hızlı | pivot etrafında parçala; ortalama Θ(n log n), en kötü Θ(n²); yerinde, kararsız |
| Yığın | O(n) max-heap kur, n − 1 kez kökü sona al ve süz; O(n log n), O(1) ek bellek |
| Doğrusal | karşılaştırmalı alt sınır Ω(n log n); saymalı Θ(n + k), basamak Θ(d(n + b)), kova beklenen Θ(n) |
Özet
Etkileşimli Simülatörler
Özet
Sık Yapılan Hatalar
j < n − 1 − i olmalı; j < n yazmak dizi[j+1] ile taşar.gecici önceden alınmazsa ilk kaydırma onu ezer.<= yerine <, saymalıda baştan sona gezmek eşit anahtarların sırasını bozar.hizliSirala(dizi, alt, p) yazmak pivotu tekrar işler; iki eleman kalınca sonsuz özyineleme olabilir.Özet
Örnek Problemler
- [5, 2, 4, 6, 1, 3] dizisine bayraklı kabarcık sıralama uygulanıyor. Her tur sonundaki diziyi yazın.
- Aynı diziyi eklemeli sıralama kaç karşılaştırma ve kaç kaydırma ile sıralar? Seçmeli sıralama kaç yer değiştirme yapar?
parcala(Lomuto, pivot = son eleman) [2, 8, 7, 1, 3, 5, 6, 4] üzerinde çalışınca dizi ve dönüş değeri ne olur?- [4, 10, 3, 5, 1] dizisinden aşağıdan yukarı kurulan max-heap nedir?
- A = [2, 5, 3, 0, 2, 3, 0, 3] (anahtarlar 0 … 5) için saymalı sıralamanın 3. döngüden sonraki C dizisi nedir?
- Birleştirmeli sıralama 8 elemanı en fazla kaç karşılaştırmayla sıralar?
Cevapları göster
- [2, 4, 5, 1, 3, 6] → [2, 4, 1, 3, 5, 6] → [2, 1, 3, 4, 5, 6] → [1, 2, 3, 4, 5, 6] → [1, 2, 3, 4, 5, 6] (son turda takas yok → durur).
- Eklemeli: 12 karşılaştırma, 9 kaydırma (= ters çift sayısı). Seçmeli: 15 karşılaştırma, 3 yer değiştirme.
- [2, 1, 3, 4, 7, 5, 6, 8], dönüş 3 (pivot 4 indis 3'te).
- [10, 5, 3, 4, 1]: önce i = 1 (10 zaten büyük), sonra i = 0: 4 ↔ 10, 4 ↔ 5.
- C = [2, 2, 4, 7, 7, 8] (adetler [2, 0, 2, 3, 0, 1] ve önek toplamları).
- Her düzeyde birleştirilen m eleman en fazla m − 1 karşılaştırma ister: 4·1 + 2·3 + 1·7 = 17.
Özet
Alıştırmalar
- n = 6 elemanlı ters sıralı dizide kabarcık sıralama kaç karşılaştırma ve kaç yer değiştirme yapar?
- Seçmeli sıralamanın kararsız olduğunu gösteren üç elemanlı bir örnek verin.
- Eklemeli sıralamada doğru konumu ikili arama ile bulursak zaman karmaşıklığı ne olur?
- Hızlı sıralamayı (son eleman pivot) zaten sıralı 1000 elemanlı diziyle çağırınca ne olur? Nasıl düzeltilir?
- [329, 457, 657, 839, 436, 720, 355] dizisini LSD basamak sıralama ile sıralayın; her geçişten sonraki diziyi yazın.
- Bir dizideki ters çift sayısını O(n log n)'de bulan bir yöntem önerin.
Cevaplar / ipuçları
- Her tur tüm çiftler ters: 5 + 4 + 3 + 2 + 1 = 15 karşılaştırma ve 15 yer değiştirme.
- [2ₐ, 2ᵦ, 1] → ilk turda 1 ile 2ₐ yer değiştirir → [1, 2ᵦ, 2ₐ].
- Karşılaştırmalar O(n log n)'e iner ama kaydırmalar hâlâ Θ(n²) → toplam yine Θ(n²).
- Her parçalama yalnızca pivotu ayırır: 999 + 998 + … + 1 = 499.500 karşılaştırma, 1000 derinlikli özyineleme. Rastgele pivot ya da üçün ortancası.
- Birler: [720, 355, 436, 457, 657, 329, 839]; onlar: [720, 329, 436, 839, 355, 457, 657]; yüzler: [329, 355, 436, 457, 657, 720, 839].
- Birleştirmeli sıralamayı değiştirin: sağdan bir eleman alındığında sol yarıda kalan eleman sayısı (L.length − i) kadar ters çift eklenir; toplam O(n log n).
Özet
Kaynaklar
- Cormen, Leiserson, Rivest ve Stein, Introduction to Algorithms, 4. baskı, MIT Press, 2022 (Bölüm 2, 6, 7, 8).
- Sedgewick ve Wayne, Algorithms, 4. baskı, Addison-Wesley, 2011 (Bölüm 2: Sorting).
- D. E. Knuth, The Art of Computer Programming, Vol. 3: Sorting and Searching, 2. baskı, 1998.
- E. D. Demaine ve C. E. Leiserson, MIT 6.046J Introduction to Algorithms ders notları, 2005 (parçalama ve saymalı sıralama örnekleri).
- C. A. R. Hoare, "Quicksort", The Computer Journal 5(1), 1962; D. L. Shell, "A High-Speed Sorting Procedure", CACM 2(7), 1959.
- T. Peters, "listsort.txt" (Timsort tasarım notu), CPython, 2002.