Bölüm 19: Temel Sıralama Algoritmaları
Giriş
Öğrenme Çıktıları
Kayıt, anahtar, iç/dış sıralama, kararlılık ve yerinde sıralamayı ayırt etmek.
Seçimli, eklemeli ve kabarcık sıralamasını C'de yazıp adım adım izlemek.
Karşılaştırma/değiştirme sayısını; en iyi, en kötü ve ortalama durumu hesaplamak.
Saymalı (counting) ve kova (bucket) sıralamanın ne zaman O(n) olduğunu açıklamak.
Aralıklı eklemeli sıralamanın neden eklemeliden hızlı olduğunu göstermek.
Karar ağacıyla karşılaştırmalı sıralamanın Ω(n lg n) sınırını kanıtlamak.
Sıralama Kavramları
Sıralama: Kayıt, Anahtar, İç/Dış
- Alanların (fields) oluşturduğu kayıtlar (records) bellekte tutulunca liste, bellek dışında tutulunca kütük (file) adını alır.
- Kaydı biricik (unique) olarak tanıtan alan(lar) anahtardır (key); sıralama anahtara göre yapılır.
- İç sıralama (internal): verinin tamamı bellekte. Dış sıralama (external): disk/teyp gibi yardımcı bellek kullanılır; disk erişim sayısı belirleyicidir.
Anahtar: öğrenci no
Sıralama Kavramları
Kararlılık (Stability) ve Yerinde (In-place) Sıralama
Eşit anahtarlı kayıtların göreli sırası korunur. Önce ada, sonra soyada göre kararlı sıralanırsa soyadı aynı olanlar ad sırasında kalır (basamak sıralamanın temeli).
Girdinin dışında yalnızca O(1) (bazı tanımlarda O(lg n)) ek bellek kullanır. Seçimli, eklemeli, kabarcık yerindedir; saymalı sıralama değildir (c ve b dizileri).
Sıralama Kavramları
Ortak Altyapı: kucuk ve SWAP
#define N 15 #define kucuk(A, B) ((A) < (B)) #define SWAP(A, B) do { int t_ = (A); (A) = (B); (B) = t_; } while (0)
int main(void) { int *a = malloc(N * sizeof *a); if (a == NULL) return 1; for (int i = 0; i < N; i++) a[i] = rand() % 50; selection(a, 0, N - 1); /* sıralama yordamına çağrı */ for (int i = 0; i < N; i++) printf("%d ", a[i]); free(a); return 0; }
Makroyu {int t = A; A=B; B=t;} biçiminde yazmak if (…) SWAP(…); else içinde derleme hatası verir; do { … } while (0) bu tuzağı kapatır. if (kucuk(A, B)) SWAP(A, B); kalıbı A < B ise takas eder (karşılaştır-takas).
Seçimli Sıralama
Seçimli Sıralama (Selection Sort)
void selection(int a[], int L, int R) { for (int i = L; i < R; i++) { int min = i; for (int j = i + 1; j <= R; j++) if (kucuk(a[j], a[min])) min = j; SWAP(a[i], a[min]); } }
- Listedeki en küçük değerli öğe bulunur.
- Bu öğe ilk konumdaki öğeyle yer değiştirir.
- Aynı adımlar 2. öğeden başlayarak kalan alt liste için yinelenir.
Her dış tur sonunda a[L..i] sıralı ve dizinin en küçük öğelerini içerir.
En küçüğü seçmek için kalan alt liste kesinlikle sonuna kadar taranmalıdır; veri sıralı olsa bile.
Seçimli Sıralama · Kodu adım adım çalıştır
Kod İzleme: selection(a, 0, 4)
Tur i = 0: kalan alt liste a[0..4]. Başlangıçta en küçüğün indisi min = 0 (a[0] = 29).
a[1] = 10 < a[0] = 29 → yeni en küçük: min = 1.
a[2] = 14 ≥ a[1] = 10 → min değişmez.
a[3] = 37 ≥ a[1] = 10 → min değişmez.
a[4] = 13 ≥ a[1] = 10 → min değişmez.
SWAP(a[0], a[1]): 10 yerine oturdu; a[0..0] artık sıralı.
Tur i = 1: kalan alt liste a[1..4]. Başlangıçta en küçüğün indisi min = 1 (a[1] = 29).
a[2] = 14 < a[1] = 29 → yeni en küçük: min = 2.
a[3] = 37 ≥ a[2] = 14 → min değişmez.
a[4] = 13 < a[2] = 14 → yeni en küçük: min = 4.
SWAP(a[1], a[4]): 13 yerine oturdu; a[0..1] artık sıralı.
Tur i = 2: kalan alt liste a[2..4]. Başlangıçta en küçüğün indisi min = 2 (a[2] = 14).
a[3] = 37 ≥ a[2] = 14 → min değişmez.
a[4] = 29 ≥ a[2] = 14 → min değişmez.
min = i olduğundan takas kendisiyle yapılır (değişiklik yok); a[0..2] sıralı.
Tur i = 3: kalan alt liste a[3..4]. Başlangıçta en küçüğün indisi min = 3 (a[3] = 37).
a[4] = 29 < a[3] = 37 → yeni en küçük: min = 4.
SWAP(a[3], a[4]): 29 yerine oturdu; a[0..3] artık sıralı.
void selection(int a[], int L, int R) { for (int i = L; i < R; i++) { int min = i; for (int j = i + 1; j <= R; j++) if (kucuk(a[j], a[min])) min = j; SWAP(a[i], a[min]); } }
Seçimli Sıralama
Seçimli Sıralamanın Analizi
(n−1) + (n−2) + … + 2 + 1 = n(n−1)/2 ≈ n²/2
- Karşılaştırma sayısı girdiden bağımsızdır: en iyi = ortalama = en kötü ≈ n²/2.
- Değiştirme (takas) sayısı n − 1: son öğeye gelindiğinde o zaten doğru yerdedir.
- Karmaşıklık O(n²); ek bellek O(1) (yerinde).
- Uzak takaslar yüzünden kararlı değildir (önceki 3a/3c örneği).
| n | karşılaştırma | takas |
|---|---|---|
| 5 | 10 | 4 |
| 10 | 45 | 9 |
| 100 | 4950 | 99 |
| 1000 | 499500 | 999 |
Eklemeli Sıralama
Eklemeli Sıralama (Insertion Sort): İki Sürüm
void insertion(int a[], int L, int R) { for (int i = L + 1; i <= R; i++) { int v = a[i]; // yerleştirilecek int j = i - 1; // a[L..i-1] sıralı while (j >= L && a[j] > v) { a[j + 1] = a[j]; // sağa kaydır j--; } a[j + 1] = v; } }
void insertion1(int a[], int L, int R) { for (int i = R; i > L; i--) // bekçi if (a[i] < a[i-1]) SWAP(a[i], a[i-1]); for (int i = L + 2; i <= R; i++) { int v = a[i], j = i; while (kucuk(v, a[j - 1])) { a[j] = a[j - 1]; j--; } a[j] = v; } }
- Her turda girdiden bir öğe alınır ve sıralı ön ek içinde uygun konuma eklenir (iskambil kâğıdı dizer gibi).
- Sağdaki
insertion1, soldakinin ilerlemiş hâlidir: önce en küçüğüa[L]'ye taşır; bu bekçi (sentinel) sayesinde iç döngüdej > Lsınaması gerekmez. - Koşulu
j >= 0yazmak yanlıştır; alt diziL > 0'dan başlarsa sınırı aşar, doğrusuj >= L.
Eklemeli Sıralama · Kodu adım adım çalıştır
Kod İzleme: insertion(a, 0, 4)
i = 1: v = a[1] = 2 bir kenara alındı; a[0..0] sıralı, v için yer aranacak.
a[0] = 7 > v = 2 → 7 bir sağa kaydı (a[1]); j = -1.
j = −1, dizinin başına gelindi → döngü biter; a[0] = 2. Sıralı ön ek artık a[0..1].
i = 2: v = a[2] = 9 bir kenara alındı; a[0..1] sıralı, v için yer aranacak.
a[1] = 7 ≤ v → döngü biter; a[2] = 9. Sıralı ön ek artık a[0..2].
i = 3: v = a[3] = 5 bir kenara alındı; a[0..2] sıralı, v için yer aranacak.
a[2] = 9 > v = 5 → 9 bir sağa kaydı (a[3]); j = 1.
a[1] = 7 > v = 5 → 7 bir sağa kaydı (a[2]); j = 0.
a[0] = 2 ≤ v → döngü biter; a[1] = 5. Sıralı ön ek artık a[0..3].
i = 4: v = a[4] = 1 bir kenara alındı; a[0..3] sıralı, v için yer aranacak.
a[3] = 9 > v = 1 → 9 bir sağa kaydı (a[4]); j = 2.
a[2] = 7 > v = 1 → 7 bir sağa kaydı (a[3]); j = 1.
a[1] = 5 > v = 1 → 5 bir sağa kaydı (a[2]); j = 0.
a[0] = 2 > v = 1 → 2 bir sağa kaydı (a[1]); j = -1.
j = −1, dizinin başına gelindi → döngü biter; a[0] = 1. Sıralı ön ek artık a[0..4].
void insertion(int a[], int L, int R) { for (int i = L + 1; i <= R; i++) { int v = a[i]; // yerleştirilecek int j = i - 1; // a[L..i-1] sıralı while (j >= L && a[j] > v) { a[j + 1] = a[j]; // sağa kaydır j--; } a[j + 1] = v; } }
Eklemeli Sıralama · Kodu adım adım çalıştır
Kod İzleme: insertion1(a, 0, 3) (bekçili)
a[3] = 2 ≥ a[2] = 1 → takas yok. En küçük öğe sola doğru ilerliyor.
a[2] = 1 < a[1] = 3 → takas. En küçük öğe sola doğru ilerliyor.
a[1] = 1 < a[0] = 4 → takas. a[0] = 1 artık en küçük: bekçi hazır.
i = 2: v = 3. a[0..1] sıralı.
v = 3 < a[1] = 4 → kaydır; j = 1. Sınır sınaması yok: bekçi a[0] durduracak.
a[0] = 1 ≤ v → dur; a[1] = 3.
i = 3: v = 2. a[0..2] sıralı.
v = 2 < a[2] = 4 → kaydır; j = 2. Sınır sınaması yok: bekçi a[0] durduracak.
v = 2 < a[1] = 3 → kaydır; j = 1. Sınır sınaması yok: bekçi a[0] durduracak.
a[0] = 1 ≤ v → dur; a[1] = 2.
void insertion1(int a[], int L, int R) { for (int i = R; i > L; i--) // bekçi if (a[i] < a[i-1]) SWAP(a[i], a[i-1]); for (int i = L + 2; i <= R; i++) { int v = a[i], j = i; while (kucuk(v, a[j - 1])) { a[j] = a[j - 1]; j--; } a[j] = v; } }
Eklemeli Sıralama
Eklemeli Sıralama: En İyi ve En Kötü Durum
Girdi sıralıysa seçilen öğe yalnızca sıralı listenin en sağındaki değerle karşılaştırılır, daha büyük görülünce hemen yerine konur: toplam n − 1 karşılaştırma.
Girdi ters sıralıysa her öğe sıralı listenin tümüyle karşılaştırılıp en sola eklenir; sıralı dizi her turda 1 sağa kayar: 1 + 2 + … + (n−1) = n(n−1)/2.
a[j] > v katı eşitsizlik: eşit öğe atlanmaz) ve yerindedir. Sıralıya yakın veride neredeyse doğrusaldır.Eklemeli Sıralama
Ortalama Durum Analizi: j = 3 İçin Olası Durumlar
Durum 1: 8 > 10 yanlış → 1 karşılaştırma.
Durum 2: 8 > 7 doğru, 6 > 7 yanlış → 2 karşılaştırma.
Durum 3: 8 > 5 doğru, 6 > 5 doğru, 3 > 5 yanlış → 3 karşılaştırma.
Durum 4: 8 > 2 doğru, 6 > 2 doğru, 3 > 2 doğru; j = −1 olunca j >= 0 yanlış, a[j] > v hiç sınanmaz — sondan bir önceki durumla aynı sayıda → 3 karşılaştırma.
Eklemeli Sıralama
Ortalama Durum: Beklenen Karşılaştırma Sayısı
E(Cj) = (1/(j+1))·[2j + Σk=1j−1 k] = (j+2)/2 − 1/(j+1)
E(Y) = Σj=1N−1 E(Cj) = (N² + 3N)/4 − HN ≈ N²/4
- Cj: j. öğeyi yerleştirirken yapılan karşılaştırma sayısı, bir rastlantı değişkeni; E(Cj) beklenen değeri.
- HN = 1 + 1/2 + … + 1/N harmonik sayı: HN = ln N + O(1).
- Ortalamada ≈ N²/4 karşılaştırma ve ≈ N²/4 yarı değişim (takas değil kaydırma) → O(n²).
| N | N! | tüm permütasyonlar | formül | N²/4 |
|---|---|---|---|---|
| 3 | 6 | 2,667 | 2,667 | 2,25 |
| 4 | 24 | 4,917 | 4,917 | 4,00 |
| 5 | 120 | 7,717 | 7,717 | 6,25 |
| 6 | 720 | 11,050 | 11,050 | 9,00 |
| 7 | 5040 | 14,907 | 14,907 | 12,25 |
Tablo: N! dizilişin hepsi Python ile sayıldı; formülle birebir aynı.
Kabarcık Sıralaması
Kabarcık Sıralaması (Bubble Sort)
void bubble(int a[], int L, int R) { int i, j; for (i = L; i < R; i++) for (j = R; j > i; j--) if (a[j] < a[j-1]) SWAP(a[j], a[j-1]); }
Başlangıç: [7, 2, 9, 5, 1]. Her tur sağdan sola komşuları karşılaştırır.
Tur i = 0: en küçük kalan değer 1 kabarcık gibi a[0]'a yükseldi → [1, 7, 2, 9, 5].
Tur i = 1: en küçük kalan değer 2 kabarcık gibi a[1]'e yükseldi → [1, 2, 7, 5, 9].
Tur i = 2: en küçük kalan değer 5 kabarcık gibi a[2]'ye yükseldi → [1, 2, 5, 7, 9].
Tur i = 3: takas yok; kalanların en küçüğü 7 zaten a[3]'te → [1, 2, 5, 7, 9].
Kabarcık Sıralaması · Kodu adım adım çalıştır
Kod İzleme: Erken Durmalı bubble2(a, 0, 4)
Tur i = 0 başlıyor; takas bayrağı sıfırlandı.
a[4] < a[3] → takas: [2, 1, 3, 4, 5]; takas = 1.
a[3] = 4 ≥ a[2] = 3 → takas yok.
a[2] = 3 ≥ a[1] = 1 → takas yok.
a[1] < a[0] → takas: [1, 2, 3, 4, 5]; takas = 1.
takas = 1 → dizi henüz sıralı sayılamaz, sonraki tura geç.
Tur i = 1 başlıyor; takas bayrağı sıfırlandı.
a[4] = 5 ≥ a[3] = 4 → takas yok.
a[3] = 4 ≥ a[2] = 3 → takas yok.
a[2] = 3 ≥ a[1] = 2 → takas yok.
Bu turda hiç takas olmadı → dizi sıralı; erken çıkış. 2 tur boşuna yapılmadı.
void bubble2(int a[], int L, int R) { for (int i = L; i < R; i++) { int takas = 0; // bu turda takas oldu mu? for (int j = R; j > i; j--) if (kucuk(a[j], a[j - 1])) { SWAP(a[j], a[j - 1]); takas = 1; } if (!takas) return; // sıralı: erken çık } }
Kabarcık Sıralaması
Kabarcık Sıralamasının Analizi
- Karşılaştırma: her durumda n(n−1)/2 ≈ n²/2 (erken durma yoksa).
- Değiştirme: en kötü durumda (ters sıralı) n²/2; ortalamada ters çift (inversion) sayısı kadar, yani ≈ n²/4.
- Karmaşıklık O(n²); erken durmalı sürüm sıralı girdide O(n).
- Yalnız komşular takaslanır ve eşitler takaslanmaz → kararlı, yerinde.
| n | karş. | takas (en kötü) | takas (ort.) |
|---|---|---|---|
| 4 | 6 | 6 | 3 |
| 5 | 10 | 10 | 5 |
| 6 | 15 | 15 | 7,5 |
| 7 | 21 | 21 | 10,5 |
Ortalama sütunu: n! dizilişin tamamı sayıldı. Bazı kaynaklar (ör. Sedgewick) ortalama için de n²/2 değiştirme verir; sayım ortalamanın n²/4 olduğunu gösteriyor.
Kabarcık Sıralaması
Ters Çift (Inversion): Ortak Ölçü
Ters çift: i < j iken a[i] > a[j] olan çift. [7, 2, 9, 5, 1] → (7,2), (7,5), (7,1), (2,1), (9,5), (9,1), (5,1)
- Komşu takası (kabarcık) ya da bir konum kaydırma (eklemeli) tam olarak bir ters çifti giderir.
- Bu yüzden ikisinde de takas/kaydırma sayısı = ters çift sayısı: bu dizide 7 (izlerde de 7 kaydırma vardı).
- Ters sıralı dizide n(n−1)/2 ters çift; rastgele dizide ortalama n(n−1)/4 (her çift ½ olasılıkla ters).
- Komşu öğelerle çalışan her algoritma ortalamada Ω(n²): uzak takas yapan Shell ve hızlı sıralama bu duvarı aşar.
Saymalı Sıralama
Saymalı Sıralama (Counting Sort)
int counting_sort(const int a[], int b[], int r, int k) { int *c = malloc((k + 1) * sizeof *c); if (c == NULL) return -1; for (int i = 0; i <= k; i++) c[i] = 0; // L1 for (int j = 0; j <= r; j++) c[a[j]]++; // L2 for (int i = 1; i <= k; i++) // L3 c[i] += c[i - 1]; for (int j = r; j >= 0; j--) { // L4 b[c[a[j]] - 1] = a[j]; c[a[j]]--; } free(c); return 0; }
| ad | anlamı |
|---|---|
| a[] | girdi dizisi |
| b[] | sıralı çıktı dizisi |
| c[] | yardımcı sayaç dizisi (k+1 öğe) |
| r | a[0..r] sıralanır (r+1 öğe) |
| k | değerler 0…k aralığında |
- 1954'te Harold H. Seward. Aralığı bilinen tam sayıları sıralar.
- Değerler birbiriyle karşılaştırılmaz → non-comparison sort (kova sıralama da bu sınıftadır).
int c[K]derleme zamanında sabit boyut ister; buradamalloc+ kontrol +free.
Saymalı Sıralama · Kodu adım adım çalıştır
Kod İzleme: counting_sort — Sayma (L1–L3)
L1: c[0..10] sıfırlandı. Her değerden kaç tane olduğu burada sayılacak.
L2: a[0] = 3 → c[3]++ = 1.
L2: a[1] = 0 → c[0]++ = 1.
L2: a[2] = 6 → c[6]++ = 1.
L2: a[3] = 9 → c[9]++ = 1.
L2: a[4] = 4 → c[4]++ = 1.
L2: a[5] = 3 → c[3]++ = 2. (İkinci 3!)
L2: a[6] = 10 → c[10]++ = 1.
L2: a[7] = 5 → c[5]++ = 1.
L3: c[1] += c[0] → 1: “≤ 1 olan 1 öğe var”.
L3: c[2] += c[1] → 1: “≤ 2 olan 1 öğe var”.
L3: c[3] += c[2] → 3: “≤ 3 olan 3 öğe var”. 3 gelirse 2. konuma.
L3: c[4] += c[3] → 4: “≤ 4 olan 4 öğe var”. 4 gelirse 3. konuma.
L3: c[5] += c[4] → 5: “≤ 5 olan 5 öğe var”. 5 gelirse 4. konuma.
L3: c[6] += c[5] → 6: “≤ 6 olan 6 öğe var”. 6 gelirse 5. konuma.
L3: c[7] += c[6] → 6: “≤ 7 olan 6 öğe var”.
L3: c[8] += c[7] → 6: “≤ 8 olan 6 öğe var”.
L3: c[9] += c[8] → 7: “≤ 9 olan 7 öğe var”. 9 gelirse 6. konuma.
L3: c[10] += c[9] → 8: “≤ 10 olan 8 öğe var”. 10 gelirse 7. konuma.
for (int i = 0; i <= k; i++) c[i] = 0; // L1 for (int j = 0; j <= r; j++) c[a[j]]++; // L2 for (int i = 1; i <= k; i++) // L3 c[i] += c[i - 1];
Saymalı Sıralama · Kodu adım adım çalıştır
Kod İzleme (devam): L4 ile b Dizisine Yerleştirme
L3 sonu: c[x] = “x'ten küçük ya da eşit öğe sayısı”. Ör. c[9] = 7 → 9 gelirse 7 − 1 = 6. konuma.
L4: a[7] = 5, c[5] = 5 → b[4] = 5; c[5]-- = 4.
L4: a[6] = 10, c[10] = 8 → b[7] = 10; c[10]-- = 7.
L4: a[5] = 3, c[3] = 3 → b[2] = 3; c[3]-- = 2 (aynı değerli bir sonraki öğe bir sola yerleşecek).
L4: a[4] = 4, c[4] = 4 → b[3] = 4; c[4]-- = 3.
L4: a[3] = 9, c[9] = 7 → b[6] = 9; c[9]-- = 6.
L4: a[2] = 6, c[6] = 6 → b[5] = 6; c[6]-- = 5.
L4: a[1] = 0, c[0] = 1 → b[0] = 0; c[0]-- = 0.
L4: a[0] = 3, c[3] = 2 → b[1] = 3; c[3]-- = 1 (aynı değerli bir sonraki öğe bir sola yerleşecek).
Sonuç b = [0, 3, 3, 4, 5, 6, 9, 10]. Sağdan sola gezildiği için iki 3'ün göreli sırası korundu: kararlı.
for (int j = r; j >= 0; j--) { // L4 b[c[a[j]] - 1] = a[j]; c[a[j]]--; }
Saymalı Sıralama
Saymalı Sıralama: Analiz ve Sınırlar
| döngü | iş | maliyet |
|---|---|---|
| L1 | c'yi sıfırla | O(k) |
| L2 | say | O(n) |
| L3 | kümülatif toplam | O(k) |
| L4 | yerleştir | O(n) |
- Toplam O(n + k); k = O(n) ise O(n). Ek bellek O(n + k): yerinde değil.
- L4 sağdan sola ilerlediği için kararlı — basamağa göre sıralamada (radix sort) bu özellik şart.
Shell ve Kova Sıralama · Kodu adım adım çalıştır
Kod İzleme: shell_sort(a, 8)
Aralık h = 4: birbirinden 4 uzak öğeler ayrı ayrı eklemeli sıralanır.
i = 4: v = 14; 1 kez 4 geri kaydırıldı, a[0] = 14.
i = 5: v = 19; 1 kez 4 geri kaydırıldı, a[1] = 19.
i = 6: v = 27; 1 kez 4 geri kaydırıldı, a[2] = 27.
i = 7: v = 44; a[3] ≤ 44, yerinde kaldı.
Aralık h = 2: birbirinden 2 uzak öğeler ayrı ayrı eklemeli sıralanır.
i = 2: v = 27; a[0] ≤ 27, yerinde kaldı.
i = 3: v = 10; 1 kez 2 geri kaydırıldı, a[1] = 10.
i = 4: v = 35; a[2] ≤ 35, yerinde kaldı.
i = 5: v = 33; a[3] ≤ 33, yerinde kaldı.
i = 6: v = 42; a[4] ≤ 42, yerinde kaldı.
i = 7: v = 44; a[5] ≤ 44, yerinde kaldı.
Son tur h = 1: sıradan eklemeli sıralama — ama dizi artık neredeyse sıralı.
i = 1: v = 10; 1 kez 1 geri kaydırıldı, a[0] = 10.
i = 2: v = 27; a[1] ≤ 27, yerinde kaldı.
i = 3: v = 19; 1 kez 1 geri kaydırıldı, a[2] = 19.
i = 4: v = 35; a[3] ≤ 35, yerinde kaldı.
i = 5: v = 33; 1 kez 1 geri kaydırıldı, a[4] = 33.
i = 6: v = 42; a[5] ≤ 42, yerinde kaldı.
i = 7: v = 44; a[6] ≤ 44, yerinde kaldı.
void shell_sort(int a[], int n) { for (int h = n / 2; h > 0; h /= 2) // aralık for (int i = h; i < n; i++) { int v = a[i], j = i; while (j >= h && a[j - h] > v) { a[j] = a[j - h]; // h geri kaydır j -= h; } a[j] = v; } }
Shell ve Kova Sıralama
Shell Sıralama (Shell Sort, 1959)
- Eklemeli sıralamanın zayıflığı: öğe bir seferde yalnız bir konum ilerler; ters sıralıda O(n²).
- Shell: önce uzak öğeler (aralık h) sıralanır, h küçültülür; son tur h = 1 ile biter, bu yüzden sonuç her zaman doğru.
- Karmaşıklık aralık dizisine bağlıdır: h = n/2, n/4, … (Shell) için en kötü O(n²); Knuth'un 1, 4, 13, 40, … dizisi için O(n3/2).
- Yerinde; ama uzak takaslar yüzünden kararlı değildir.
Veriye ait özellikler biliniyorsa shell sort ve paket (kova) sıralama birlikte kullanılabilir.
Kısa kod + iyi pratik hız: gömülü sistemlerde ve küçük/orta dizilerde tercih edilir.
Shell ve Kova Sıralama
Kova Sıralama (Bucket Sort)
Girdi [0, 1) aralığında 10 sayı; 10 kova, her kova 0,1 genişliğinde.
Dağıt: x değeri ⌊10·x⌋ numaralı kovaya eklenir (ör. .78 → 7, .17 → 1). O(n).
Her kova kendi içinde (eklemeli sıralamayla) sıralanır: kovalar küçük olduğundan ucuz.
Kovalar sırayla birleştirilir → sıralı çıktı. Düzgün dağılımda ortalama O(n).
Shell ve Kova Sıralama
Kova Sıralama: C ile Gerçekleme
typedef struct kdugum { double x; struct kdugum *bag; } kdugum; int bucket_sort(double a[], int n) { /* 0 <= a[i] < 1 */ kdugum **kova = calloc(n, sizeof *kova); kdugum *havuz = malloc(n * sizeof *havuz); if (!kova || !havuz) { free(kova); free(havuz); return -1; } for (int i = 0; i < n; i++) { /* dağıt + sıralı ekle */ kdugum **p = &kova[(int)(a[i] * n)]; while (*p && (*p)->x <= a[i]) p = &(*p)->bag; havuz[i].x = a[i]; havuz[i].bag = *p; *p = &havuz[i]; } int k = 0; /* kovaları birleştir */ for (int b = 0; b < n; b++) for (kdugum *q = kova[b]; q; q = q->bag) a[k++] = q->x; free(kova); free(havuz); return 0; }
- n kova;
a[i]değeri⌊a[i]·n⌋numaralı bağlı listeye sıralı eklenir (kova içi eklemeli sıralama). <=sayesinde eşitler sona eklenir → kararlı. Düğümler tek birhavuzdizisinden alınır: n ayrımallocyerine iki ayırma, tekfreeçifti.
Optimal Sıralama Zamanı
Optimal Sıralama Zamanı: Karar Ağacı
Sınamalar elips, kararlar (sıralamalar) dikdörtgen. n = 3 için yaprak sayısı 3! = 6.
Girdi a = 5, b = 8, c = 2. Kök: a < b? 5 < 8 → evet.
b < c? 8 < 2 → hayır: c, b'den önce gelmeli.
a < c? 5 < 2 → hayır.
Yaprak c < a < b → 2, 5, 8. Bu girdi için 3 karşılaştırma = yolun uzunluğu.
kucuk() çağrısı, her yaprak girdinin bir dizilişi. En kötü durumdaki karşılaştırma sayısı = ağacın yüksekliği.Optimal Sıralama Zamanı
Teorem: Karşılaştırmalı Sıralamanın Alt Sınırı
- Yaprak sayısı en az n! (her diziliş bir yaprak).
- Yüksekliği h olan ikili ağacın en çok 2h yaprağı vardır: 2h ≥ n! ⇒ h ≥ lg(n!).
- Stirling: ⌈lg(n!)⌉ = n lg n − n lg e + Θ(lg n) ≈ n lg n − 1,4427 n.
- Sonuç: karşılaştırmalı sıralama Ω(n lg n); saymalı/kova/basamak sıralama karşılaştırma yapmadığı için bu sınıra takılmaz.
| n | ⌈lg n!⌉ | n lg n − 1,44n | n(n−1)/2 |
|---|---|---|---|
| 3 | 3 | 0,4 | 3 |
| 5 | 7 | 4,4 | 10 |
| 10 | 22 | 18,8 | 45 |
| 20 | 62 | 57,6 | 190 |
| 100 | 525 | 520,1 | 4950 |
Laboratuvar · C kodu adım adım
Laboratuvar: Temel Sıralamalar
Özet
Aynı Girdide Sayım (n = 1000)
| algoritma | sıralı: karş. / taşıma | ters: karş. / taşıma | rastgele: karş. / taşıma |
|---|---|---|---|
| Seçimli | 499.500 / 999 | 499.500 / 999 | 499.500 / 999 |
| Eklemeli | 999 / 0 | 499.500 / 499.500 | 247.876 / 246.884 |
| Kabarcık (erken dur.) | 999 / 0 | 499.500 / 499.500 | 499.035 / 246.884 |
| Shell (n/2, n/4, …) | 8.006 / 0 | 11.716 / 4.700 | 14.760 / 7.254 |
Değerler Python simülasyonuyla sayıldı (rastgele girdi: tohum 19). Taşıma: seçimlide takas, eklemeli/Shell'de kaydırma, kabarcıkta takas.
Girdiye kör: her durumda 499.500 karşılaştırma, 999 takas.
Sıralıda 999 karşılaştırma; rastgelede ≈ n²/4; tersinde n²/2.
Rastgele girdide eklemeliden kat kat az iş: uzak kaydırmalar ters çiftleri hızla yok eder.
Özet
Karmaşıklık Özeti
| algoritma | en iyi | ortalama | en kötü | ek bellek | kararlı | yerinde |
|---|---|---|---|---|---|---|
| Seçimli | n²/2 | n²/2 | n²/2 | O(1) | hayır | evet |
| Eklemeli | O(n) | ≈ n²/4 | n²/2 | O(1) | evet | evet |
| Kabarcık (erken dur.) | O(n) | ≈ n²/2 karş. | n²/2 | O(1) | evet | evet |
| Shell (Knuth h) | O(n lg n) | — | O(n3/2) | O(1) | hayır | evet |
| Saymalı | O(n+k) | O(n+k) | O(n+k) | O(n+k) | evet | hayır |
| Kova (düzgün dağ.) | O(n) | O(n) | O(n²) | O(n) | evet* | hayır |
Seçimli/eklemeli/kabarcık satırlarında sayılar karşılaştırma sayısıdır. * Kova içinde kararlı bir sıralama kullanılırsa. Shell ortalaması aralık dizisine bağlı ve kapalı biçimde bilinmiyor. Karşılaştırmalı sıralamada alt sınır ⌈lg n!⌉ ≈ n lg n.
Özet
Hangi Durumda Hangisi?
Seçimli: tam n − 1 takas.
Eklemeli: kararlı, yerinde, sıralıya yakında O(n); hızlı sıralamanın küçük parçalarında da kullanılır.
Kabarcık + erken durma: bir turda sıralı olduğunu anlar; üretimde nadiren.
Saymalı: O(n + k), kararlı; k ≫ n ise bellek ve zaman boşa gider.
Shell: birkaç satırlık kodla O(n²)'den belirgin hızlı.
Kova: ortalama O(n).
Özet
Sık Yapılan Hatalar
min) gerekir.v = a[i]'yi kaydırmadan önce saklamamak: ilk kaydırma a[i]'yi ezer.j >= 0 yazmak: alt dizi a[L..R] ise sınır j >= L.if (kucuk(A, B)) SWAP(A, B) kalıbında yönü karıştırmak: A < B ise takas edilir.c[] dışına yazar.Özet
Alıştırmalar
- [5, 1, 4, 2, 8] için seçimli sıralamada her tur sonundaki diziyi yazın. Kaç takas yapıldı?
- Eklemeli sıralama [1, 2, 3, …, n] ve [n, …, 2, 1] için kaç karşılaştırma yapar?
bubble2[1, 2, 3, 5, 4] girdisinde kaç tur çalışır?- a = [2, 5, 3, 0, 2, 3, 0, 3], k = 5 için counting sort'un L3 sonrası c dizisini yazın.
- 3 öğe için karar ağacının en az yüksekliği nedir? 4 öğe için?
- Seçimli sıralamanın kararsız olduğunu gösteren en küçük örneği bulun.
Cevapları göster
- [1,5,4,2,8] → [1,2,4,5,8] → [1,2,4,5,8] → [1,2,4,5,8]; 4 tur, ilk ikisi gerçek takas (kalan ikisi kendisiyle).
- Sıralıda n − 1; ters sıralıda n(n−1)/2.
- 2 tur: ilk turda 5 ile 4 takaslanır, ikinci turda takas yok → erken çıkış.
- Sayaçlar [2,0,2,3,0,1] → kümülatif [2,2,4,7,7,8].
- ⌈lg 3!⌉ = ⌈lg 6⌉ = 3; ⌈lg 4!⌉ = ⌈lg 24⌉ = 5.
- [2a, 2b, 1]: ilk turda 1 ile 2a takaslanır → [1, 2b, 2a].
Özet
Bölüm Özeti
Aynı sınıfta ama farklı karakter: seçimli az takas, eklemeli sıralıya yakında hızlı, kabarcık erken durabilir.
Tüm n! dizilişin beklenen değeri: eklemeli ≈ n²/4 karşılaştırma. En iyi ile en kötünün ortalaması değildir!
Saymalı O(n+k) ve kova ortalama O(n): veri hakkında bilgi (aralık, dağılım) hız kazandırır.
Kararlılık (eşitlerin sırası) ve yerindelik (ek bellek) algoritma seçiminde belirleyicidir.
Aralıklı eklemeli sıralama; son tur h = 1 doğruluğu garanti eder.
Karşılaştırmalı sıralama ⌈lg n!⌉ ≈ n lg n karşılaştırmanın altına inemez → ileri sıralama algoritmalarının mihenk taşı.
Özet
Kaynaklar ve Devam
- E. Horowitz, S. Sahni, S. Anderson-Freed, Fundamentals of Data Structures in C, 2. bs., Bölüm 7 (Sorting).
- R. Sedgewick, Algorithms in C, Kısım 1–4, Bölüm 6 (Elementary Sorting Methods): makro yaklaşımı, shell sort.
- T. H. Cormen vd., Introduction to Algorithms (CLRS), Bölüm 2 (insertion), 8 (alt sınır, counting, bucket).