Bölüm 20: İleri Sıralama Algoritmaları
Giriş
Öğrenme Çıktıları
Bölümleme (partition) değişmezini açıklamak; bol() ve özyinelemeyi adım adım izlemek.
En iyi n lg n, en kötü n², ortalama ≈ 1,39 n lg n sonuçlarını yineleme bağıntılarından elde etmek.
Özyinelemesiz sürüm (O(lg n) yığın), medyan-3, küçük parçada eklemeli; C'de qsort.
merge işlemini, yığın çerçevelerini ve bağlı (indisli) sürümü izlemek; O(n) ek bellek.
Aşağıdan yukarıya O(n)'de heap kurup yerinde O(n lg n) sıralamak.
LSD radix sort'u kovalı bağlı listelerle uygulamak ve karmaşıklığını yorumlamak.
Hızlı Sıralama
Hızlı (Çabuk) Sıralama: Quick Sort
- 1962'de C. A. R. (Tony) Hoare tarafından geliştirildi.
- Böl ve fethet (divide and conquer): problem doğrudan çözülecek kadar küçülene dek özyinelemeli olarak alt problemlere bölünür.
- Ortalama n lg n ile orantılı zaman; en kötü durumda n². Yalnızca küçük bir yardımcı yığın kullanır.
- Kararlı değildir; yine de bilinen en hızlı genel amaçlı algoritmalardan biridir. Standart C kitaplığında
qsortvardır.
int bol(int a[], int L, int R); void quicksort1(int a[], int L, int R) { if (R <= L) return; int i = bol(a, L, R); quicksort1(a, L, i - 1); quicksort1(a, i + 1, R); }
Hızlı Sıralama
bol(): Bölümleme (Partition)
int bol(int a[], int L, int R) { int i = L - 1, j = R, v = a[R]; // pivot a[R] for (;;) { while (kucuk(a[++i], v)) ; while (kucuk(v, a[--j])) if (j == L) break; if (i >= j) break; SWAP(a[i], a[j]); } SWAP(a[i], a[R]); return i; }
- [2] Pivot (partitionary element)
v = a[R]. - [4] i soldan, v'den büyük ya da eşit ilk öğede durur. Sınır sınaması gerekmez: en kötü i = R'de
a[R] = vdurdurur. - [6–7] j sağdan, v'den küçük ya da eşit öğede durur; bu döngüde taşmayı
j == Lönler. - [9] İki öğe yanlış taraftadır → takas. [8] i ile j buluşunca döngü biter; [11] pivot a[i]'ye: solu ≤ v ≤ sağı.
Hızlı Sıralama · Kodu adım adım çalıştır
Kod İzleme: bol(a, 0, 9)
Pivot v = a[9] = 48. i = L − 1 = −1, j = R = 9: taramalar ++i ve --j ile başlayacak.
a[0] = 37 < 48 → i ilerlemeye devam.
a[1] = 15 < 48 → i ilerlemeye devam.
a[2] = 24 < 48 → i ilerlemeye devam.
a[3] = 33 < 48 → i ilerlemeye devam.
a[4] = 49 ≥ 48 → i = 4'te durdu (sağ tarafa ait bir öğe).
a[8] = 72 > 48 → j sola ilerliyor.
a[7] = 77 > 48 → j sola ilerliyor.
a[6] = 51 > 48 → j sola ilerliyor.
a[5] = 11 ≤ 48 → j = 5'te durdu.
i < j: a[4] ile a[5] yanlış tarafta → takas: [37, 15, 24, 33, 11, 49, 51, 77, 72, 48].
a[5] = 49 ≥ 48 → i = 5'te durdu (sağ tarafa ait bir öğe).
a[4] = 11 ≤ 48 → j = 4'te durdu.
i = 5 ≥ j = 4: indisler çaprazlandı → döngüden çık.
Pivot a[5]'e taşındı ve 5 döndürüldü. Veriler sıralansaydı 48 tam burada olurdu.
int bol(int a[], int L, int R) { int i = L - 1, j = R, v = a[R]; // pivot a[R] for (;;) { while (kucuk(a[++i], v)) ; while (kucuk(v, a[--j])) if (j == L) break; if (i >= j) break; SWAP(a[i], a[j]); } SWAP(a[i], a[R]); return i; }
Hızlı Sıralama · Kodu adım adım çalıştır
Kod İzleme: quicksort1(a, 0, 9) — Çağrı Yığını
bol(0, 9): pivot 48 → konum 5. Sol a[0..4], sağ a[6..9] ayrı sıralanacak.
bol(0, 4): pivot 11 → konum 0. Sol a[0..-1], sağ a[1..4] ayrı sıralanacak.
quicksort1(0, -1): boş aralık → dön.
bol(1, 4): pivot 37 → konum 4. Sol a[1..3], sağ a[5..4] ayrı sıralanacak.
bol(1, 3): pivot 33 → konum 3. Sol a[1..2], sağ a[4..3] ayrı sıralanacak.
bol(1, 2): pivot 24 → konum 2. Sol a[1..1], sağ a[3..2] ayrı sıralanacak.
quicksort1(1, 1): tek öğe → zaten yerinde, dön.
quicksort1(3, 2): boş aralık → dön.
quicksort1(4, 3): boş aralık → dön.
quicksort1(5, 4): boş aralık → dön.
bol(6, 9): pivot 49 → konum 6. Sol a[6..5], sağ a[7..9] ayrı sıralanacak.
quicksort1(6, 5): boş aralık → dön.
bol(7, 9): pivot 51 → konum 7. Sol a[7..6], sağ a[8..9] ayrı sıralanacak.
quicksort1(7, 6): boş aralık → dön.
bol(8, 9): pivot 77 → konum 9. Sol a[8..8], sağ a[10..9] ayrı sıralanacak.
quicksort1(8, 8): tek öğe → zaten yerinde, dön.
quicksort1(10, 9): boş aralık → dön.
int bol(int a[], int L, int R); void quicksort1(int a[], int L, int R) { if (R <= L) return; int i = bol(a, L, R); quicksort1(a, L, i - 1); quicksort1(a, i + 1, R); }
Hızlı Sıralama
Değerlendirme: Neden Hızlı?
Kabarcık ve seçimli de her adımda bir öğeyi doğru konuma koyar; ama sonraki adımda alt problem n − 1 kalır. Hızlı sıralama hem pivotu yerleştirir hem de problemi iki küçük parçaya böler.
quicksort1(a, 0, 9) izinde 10 öğe için 17 çağrı: her iç çağrı bir pivotu kesin yerine koydu (izde yeşil hücreler).
Hızlı Sıralama · Okuma Parçası
Okuma Parçası: Algoritma ve Bilgisayar
Algoritma kelimesi nerden geliyor? 8. yüzyılda el-Harezmî'nin 4GB'lık laptopu mu vardı? ☺”
Bağdat'taki Beytü'l-Hikme'de çalışan matematikçi ve gökbilimci. Birinci ve ikinci dereceden denklemlerin sistematik çözümünü veren el-Kitâbü'l-Muhtasar fî Hisâbi'l-Cebr ve'l-Mukâbele kitabının yazarı.
“Cebir/algebra” bu kitaptaki el-cebr yönteminden; “algoritma” ise adının Latince biçimi Algoritmi'den gelir. Onluk konumsal sayı sistemini Batı'ya tanıtan eserleriyle bilinir.
Hızlı Sıralama
Quick Sort: En Kötü ve En İyi Durum
Pivot a[R] her seferinde en büyüktür, yerinde kalır; sonraki çağrı bir eksik boyutla yapılır:
n + (n−1) + … + 1 = n(n+1)/2 = O(n²)
Her adımda i ve j toplam n adım ilerler, n/2'lik iki alt problem kalır:
Cn = 2Cn/2 + n = 2kCn/2k + k·n = n lg n
Hızlı Sıralama · Sayısal doğrulama
Sayım: Sıralı ve Rastgele Girdide quicksort1
| n | sıralı: karşılaştırma | derinlik | rastgele: karşılaştırma | derinlik | n lg n |
|---|---|---|---|---|---|
| 10 | 63 | 9 | 36 | 5 | 33 |
| 100 | 5.148 | 99 | 721 | 9 | 664 |
| 1000 | 501.498 | 999 | 12.831 | 25 | 9.966 |
bol() içindeki karşılaştırmalar Python simülasyonuyla sayıldı (rastgele girdi: tohum 20).
Karşılaştırma ≈ n²/2 ve özyineleme derinliği n − 1: C'de büyük n için yığın taşması riski.
Karşılaştırma n lg n mertebesinde, derinlik yalnızca birkaç lg n.
Hızlı Sıralama
Ortalama Durum Analizi: Yineleme Bağıntısı
Cn = (n + 1) + (1/n) Σk=1n (Ck−1 + Cn−k), C0 = C1 = 0
- Bölme noktası k, 1 ≤ k ≤ n arasında eşit olasılıklı: n! dizilişin hepsinde bol() çağrılınca frekanslar eşit çıkar.
- n + 1: bol()'da pivotla yapılan karşılaştırmalar; k'da bölünürse (k−1) ve (n−k) öğeli iki alt dizi kalır.
- İki toplam aynı terimleri içerir: Σ(Ck−1 + Cn−k) = 2 Σk=0n−1 Ck ⇒ n·Cn = n(n+1) + 2 Σ Ck−1 … (1)
Hızlı Sıralama
Ortalama Durum: Çıkarma Hilesi ve Sonuç
- (1)'de n yerine n − 1: (n−1)Cn−1 = n(n−1) + 2 Σk=1n−1 Ck−1 … (2)
- (1) − (2): n·Cn − (n−1)Cn−1 = 2n + 2Cn−1 ⇒ n·Cn = (n+1)Cn−1 + 2n
- (n(n+1)'e bölünce) Cn/(n+1) = Cn−1/n + 2/(n+1): artık iterasyonla açılabilir.
- Açınca Cn ≈ 2(n+1)(1/3 + 1/4 + … + 1/(n+1)) ≤ 2(n+1) ln n.
Cn ≈ 2n ln n = 2 ln 2 · n lg n ≈ 1,39 n lg n
| n | Cn (bağıntıdan) | 1,39 n lg n | oran |
|---|---|---|---|
| 10 | 37,1 | 46,2 | 0,80 |
| 100 | 780,5 | 923,5 | 0,85 |
| 1000 | 12.318,6 | 13.852,4 | 0,89 |
Tablo: bağıntı Python ile tam hesaplandı. Oran yavaşça 1'e yaklaşır (alt dereceli −cn terimi).
Hızlı Sıralamayı İyileştirmek
Özyinelemesiz Quick Sort (quicksort2)
void quicksort2(int a[], int L, int R) { yigit_baslat(); yigita_at(L, R); while (!yigit_bosmu()) { L = yigit_al(); R = yigit_al(); if (R <= L) continue; int i = bol(a, L, R); if (i - L > R - i) { // büyük parça önce yigita_at(L, i - 1); yigita_at(i + 1, R); } else { yigita_at(i + 1, R); yigita_at(L, i - 1); } } }
#define YMAX 64 /* > 2·lg n */ static int yigit[YMAX], ust; void yigit_baslat(void) { ust = 0; } int yigit_bosmu(void) { return ust == 0; } void yigit_ekle(int x) { yigit[ust++] = x; } int yigit_al(void) { return yigit[--ust]; } #define yigita_at(A, B) \ do { yigit_ekle(B); yigit_ekle(A); } while (0)
- Amaç: sistem yığını yerine kendi yığınımızı kullanmak.
yigita_at(A,B)önce B'yi sonra A'yı iter; böylece ilk alınan L olur. - [7–11] Büyük parça önce itilir, küçük parça sonra: son eklenen önce alındığından hep küçük parça işlenir → yığın derinliği O(lg n)'de kalır (en çok ≈ lg n çift).
- Bellek bağıntısı Tn = Tn/2 + 2 ⇒ Tn = 2 lg n (ikili aramadaki gibi).
Hızlı Sıralamayı İyileştirmek · Kodu adım adım çalıştır
Kod İzleme: quicksort2(a, 0, 6) — Açık Yığın
Yığına ilk problem (0, 6) itildi: önce R = 6, sonra L = 0.
(0, 6): bol → pivot 3 konum 2. sağ (3,6) büyük/eşit → önce o, sonra sol (0,1) itildi; küçük parça üstte.
(0, 1): bol → pivot 2 konum 1. sol (0,0) büyük → önce o, sonra sağ (2,1) itildi; küçük parça üstte.
(2, 1) alındı: boş aralık → continue.
(0, 0) alındı: tek öğe → continue.
(3, 6): bol → pivot 7 konum 6. sol (3,5) büyük → önce o, sonra sağ (7,6) itildi; küçük parça üstte.
(7, 6) alındı: boş aralık → continue.
(3, 5): bol → pivot 5 konum 4. sağ (5,5) büyük/eşit → önce o, sonra sol (3,3) itildi; küçük parça üstte.
(3, 3) alındı: tek öğe → continue.
(5, 5) alındı: tek öğe → continue.
Yığın boş → bitti: [1, 2, 3, 4, 5, 6, 7]. Yığında en çok 3 (L, R) çifti bekledi.
void quicksort2(int a[], int L, int R) { yigit_baslat(); yigita_at(L, R); while (!yigit_bosmu()) { L = yigit_al(); R = yigit_al(); if (R <= L) continue; int i = bol(a, L, R); if (i - L > R - i) { // büyük parça önce yigita_at(L, i - 1); yigita_at(i + 1, R); } else { yigita_at(i + 1, R); yigita_at(L, i - 1); } } }
Hızlı Sıralamayı İyileştirmek
Geliştirilmiş Quick Sort: Medyan-3 + Eklemeli
#define M 10 /* bu boydan küçük parça kalsın */ void quicksort3(int a[], int L, int R) { if (R - L <= M) return; SWAP(a[(L + R) / 2], a[R - 1]); if (a[R-1] < a[L]) SWAP(a[R-1], a[L]); // a[L] <= a[R-1] if (a[R] < a[L]) SWAP(a[R], a[L]); // a[L] <= a[R] if (a[R] < a[R-1]) SWAP(a[R], a[R-1]); // a[R-1] <= a[R] int i = bol(a, L + 1, R - 1); quicksort3(a, L, i - 1); quicksort3(a, i + 1, R); } void sirala(int a[], int L, int R) { quicksort3(a, L, R); insertion(a, L, R); /* kalan küçük parçalar */ }
if (kucuk(A, B)) SWAP(A, B) “A < B ise takas” demektir; argümanlar (a[L], a[R-1]) sırasıyla verilirse büyüğü a[L]'ye atar ve bölümlemeyi bozar (rastgele dizilerle denendi: pivotun solunda ondan büyük öğe kalıyor). Bu yüzden argümanlar ters sırada verilir: sonuçta a[L] ≤ a[R−1] ≤ a[R].Hızlı Sıralamayı İyileştirmek
Neden Medyan-3 ve Küçük Parça Eşiği?
a[L], ortadaki öğe ve a[R] sıraya konur; ortancası a[R−1]'e alınıp pivot olur. Quick sort en kötü performansını, insertion en iyi performansını sıralı veride gösteriyordu: sıralı girdideki n² durumu bozulur.
a[L] ≤ pivot ≤ a[R] olduğundan bölümleme L+1..R−1 üzerinde yapılır; uç öğeler taramaları durdurur.
R − L ≤ M olan parçalar sıralanmadan bırakılır; sirala() sonunda tek bir insertion geçişi hepsini neredeyse doğrusal sürede düzeltir. 5 ≤ M ≤ 25 için ≈ %10 kazanç.
Medyan-3 sıralı/ters sıralı girdiyi kurtarır; ama özel olarak kurulmuş kötü girdiler hâlâ n² yapabilir → çare introsort.
Hızlı Sıralamayı İyileştirmek · Kodu adım adım çalıştır
Kod İzleme: Medyan-3 Hazırlığı, quicksort3(a, 0, 8)
R − L = 8 > M = 3 (izde M = 3 alındı) → parça büyük, bölümlenecek.
Ortadaki öğe a[4] ile a[R−1] = a[7] takaslandı: aday pivot 9 artık a[7]'de.
a[7] = 9 ≥ a[0] = 8 → takas yok.
a[8] = 4 < a[0] = 8 → takas.
a[8] = 8 < a[7] = 9 → takas.
a[L] = 4 ≤ pivot 8 ≤ a[R] = 9. bol(a, 1, 7) → pivot konum 7; solu ≤ 8 ≤ sağı.
Sağ a[8..8] hemen döner. Sol a[0..6]'da R − L = 6 > M: medyan-3 yinelenir, bol(a, 1, 5) → 3 ve dizi [1, 3, 2, 4, 6, 5, 7, 8, 9] olur. Kalan parçalar (R − L = 2 ≤ M) sıralanmadan döner; sirala() içindeki insertion bitirir.
void quicksort3(int a[], int L, int R) { if (R - L <= M) return; SWAP(a[(L + R) / 2], a[R - 1]); if (a[R-1] < a[L]) SWAP(a[R-1], a[L]); // a[L] <= a[R-1] if (a[R] < a[L]) SWAP(a[R], a[L]); // a[L] <= a[R] if (a[R] < a[R-1]) SWAP(a[R], a[R-1]); // a[R-1] <= a[R] int i = bol(a, L + 1, R - 1); quicksort3(a, L, i - 1); quicksort3(a, i + 1, R); }
Okuma Parçası
Okuma Parçası: Zorunlu Stajlar
C Kitaplığı ve Hibrit · Kodu adım adım çalıştır
Kod İzleme: qsort ve Karşılaştırma Fonksiyonu
Dizi hazır; sizeof a / sizeof a[0] = 20 / 4 = 5 öğe (int 4 bayt varsayımıyla).
qsort'a dizinin adresi, öğe sayısı, öğe boyu ve karşılaştırma fonksiyonunun adresi verilir. qsort öğe türünü bilmez; void * ile çalışır.
Örnek çağrı artan(&37, &15) → 1: pozitif = ilk öğe sonra gelmeli. (qsort'un hangi çiftleri hangi sırayla karşılaştıracağı standartta belirtilmez.)
Örnek çağrı artan(&15, &24) → -1: negatif = ilk öğe önce gelmeli. (qsort'un hangi çiftleri hangi sırayla karşılaştıracağı standartta belirtilmez.)
Örnek çağrı artan(&24, &24) → 0: sıfır = eşit (göreli sıra garanti değil). (qsort'un hangi çiftleri hangi sırayla karşılaştıracağı standartta belirtilmez.)
qsort döndü: dizi artan sırada.
printf: a[0] = 15.
printf: a[1] = 24.
printf: a[2] = 33.
printf: a[3] = 37.
printf: a[4] = 49.
int artan(const void *p, const void *q) { int x = *(const int *)p; int y = *(const int *)q; return (x > y) - (x < y); // -1, 0, +1 } int main(void) { int a[] = {37, 15, 24, 33, 49}; size_t n = sizeof a / sizeof a[0]; qsort(a, n, sizeof a[0], artan); for (size_t i = 0; i < n; i++) printf("%d ", a[i]); return 0; }
C Kitaplığı ve Hibrit
qsort ile Kayıt (struct) Sıralamak
typedef struct { char ad[20]; int not_; } ogrenci; int ada_gore(const void *p, const void *q) { const ogrenci *x = p, *y = q; return strcmp(x->ad, y->ad); // alfabetik } int nota_gore_azalan(const void *p, const void *q) { const ogrenci *x = p, *y = q; return (y->not_ > x->not_) - (y->not_ < x->not_); } /* qsort(s, n, sizeof s[0], ada_gore); */
Karşılaştırıcı negatif / sıfır / pozitif döndürür ve tutarlı olmalıdır (a < b ve b < c ise a < c).
return x - y; kısa ama INT_MIN ile 1 gibi değerlerde taşar; (x > y) - (x < y) güvenlidir.
Standart, qsort'un kararlı olmasını ya da belirli bir algoritmayı kullanmasını istemez; eşitlerde sıra önemliyse ikincil anahtar eklenir.
C Kitaplığı ve Hibrit
Hibrit Sıralamalar: Algoritmaları Birleştirmek
Küçük parçalarda özyinelemenin maliyeti baskındır; M'den küçük parçalar eklemeliyle bitirilir (quicksort3 + sirala).
Hızlı sıralama ile başlar; özyineleme derinliği ≈ 2⌊lg n⌋'i aşarsa o parçaya heap sort uygular → en kötü durum O(n lg n). Küçük parçalar eklemeliyle. C++ std::sort gerçeklemelerinde yaygındır.
Birleştirme + eklemeli sıralama; veride hazır sıralı “koşuları” (run) bulup birleştirir. Kararlı; Python'un sort'u ve Java'nın nesne dizileri için Arrays.sort'u.
Büyük ölçekte iyi asimptotik davranış (n lg n) + küçük ölçekte düşük sabit (eklemeli) + en kötü durum sigortası.
Birleştirme Sıralaması
Birleştirme Sıralaması (Merge Sort)
- Liste 0 ya da 1 öğeliyse zaten sıralıdır.
- Değilse liste olabildiğince eşit iki parçaya ayrılır.
- Her parça merge sort özyinelemeli uygulanarak sıralanır.
- Kendi içinde sıralı iki alt liste tek sıralı listede birleştirilir.
- 1945, John von Neumann; böl ve fethet.
- Fikir 1: küçük listeyi sıralamak daha az adım ister.
- Fikir 2: sıralı iki listeden sıralı liste elde etmek, sırasız ikisinden elde etmekten ucuzdur.
void mergesort(int a[], int b[], int L, int R) { if (R <= L) return; int m = (R + L) / 2; mergesort(a, b, L, m); mergesort(a, b, m + 1, R); merge(a, b, L, m, R); }
void merge(int a[], int b[], int L, int m, int R) { int i, j, k; for (i = m + 1; i > L; i--) b[i - 1] = a[i - 1]; for (j = m; j < R; j++) b[R + m - j] = a[j + 1]; for (k = L; k <= R; k++) if (kucuk(b[j], b[i])) a[k] = b[j--]; else a[k] = b[i++]; }
Yardımcı b[] dizisi parametre olarak geçirilir (a ile aynı boyda); tanımlanmadan kullanılamaz.
Birleştirme Sıralaması · Kodu adım adım çalıştır
Kod İzleme: merge(a, b, 0, 2, 6)
1. döngü: sol yarı a[0..2] olduğu gibi b[0..2]'ye kopyalandı; döngü i = L = 0 ile çıkar.
2. döngü: sağ yarı b[3..6]'ya ters sırada kopyalandı → b iki uçtan ortaya doğru artan (bitonik). Döngü j = R = 6 ile çıkar.
b[j] = 4 ≥ b[i] = 3 → a[0] = b[0]; i++.
b[j] = 4 < b[i] = 5 → a[1] = b[6]; j--.
b[j] = 5 ≥ b[i] = 5 → a[2] = b[1]; i++.
b[j] = 5 < b[i] = 7 → a[3] = b[5]; j--.
b[j] = 6 < b[i] = 7 → a[4] = b[4]; j--.
b[j] = 10 ≥ b[i] = 7 → a[5] = b[2]; i++.
b[j] = 10 ≥ b[i] = 10 → a[6] = b[3]; i++.
Bitti: a = [3, 4, 5, 5, 6, 7, 10]. Sonda i = 4, j = 3: her iki indis ortada buluştu; sınır sınaması gerekmedi. Eşit 5'lerde soldaki (b[i]) önce alındı.
void merge(int a[], int b[], int L, int m, int R) { int i, j, k; for (i = m + 1; i > L; i--) b[i - 1] = a[i - 1]; for (j = m; j < R; j++) b[R + m - j] = a[j + 1]; for (k = L; k <= R; k++) if (kucuk(b[j], b[i])) a[k] = b[j--]; else a[k] = b[i++]; }
Birleştirme Sıralaması tablosu · Kodu adım adım çalıştır
Kod İzleme: mergesort — 9 merge Çağrısı
mergesort(a, b, 0, 9): önce sol yarı tamamen sıralanır; ilk merge ancak en derindeki (0, 1) çağrısında olur.
merge #1: a[0..0] ile a[1..1] birleşti → a[0..1] sıralı.
merge #2: a[0..1] ile a[2..2] birleşti → a[0..2] sıralı.
merge #3: a[3..3] ile a[4..4] birleşti → a[3..4] sıralı.
merge #4: a[0..2] ile a[3..4] birleşti → a[0..4] sıralı.
merge #5: a[5..5] ile a[6..6] birleşti → a[5..6] sıralı.
merge #6: a[5..6] ile a[7..7] birleşti → a[5..7] sıralı.
merge #7: a[8..8] ile a[9..9] birleşti → a[8..9] sıralı.
merge #8: a[5..7] ile a[8..9] birleşti → a[5..9] sıralı.
merge #9: a[0..4] ile a[5..9] birleşti → a[0..9] sıralı.
void mergesort(int a[], int b[], int L, int R) { if (R <= L) return; int m = (R + L) / 2; mergesort(a, b, L, m); mergesort(a, b, m + 1, R); merge(a, b, L, m, R); }
Birleştirme Sıralaması
Yığın Çerçevesi (Stack Activation Frame) Ağacı
Kutu: L, R, m · turuncu: merge çağrısı yapan çerçeve, #k: kaçıncı merge. Soldan derine iner, dönüşte birleştirir.
Birleştirme Sıralaması
Soru: İki mergesort Satırının Yeri Değişirse?
mergesort satırının yerleri değiştirilirse nasıl bir tablo oluşur, gözlemleyiniz.Cevap: önce sağ yarılar birleşir; sonuç aynı, ara tablolar farklı
| merge | aralık | dizi |
|---|---|---|
| #1 | (8,9) | 89 67 59 62 23 93 24 20 28 60 |
| #2 | (5,6) | 89 67 59 62 23 24 93 20 28 60 |
| #3 | (5,7) | 89 67 59 62 23 20 24 93 28 60 |
| #4 | (5,9) | 89 67 59 62 23 20 24 28 60 93 |
| #5 | (3,4) | 89 67 59 23 62 20 24 28 60 93 |
| #6 | (0,1) | 67 89 59 23 62 20 24 28 60 93 |
| #7 | (0,2) | 59 67 89 23 62 20 24 28 60 93 |
| #8 | (0,4) | 23 59 62 67 89 20 24 28 60 93 |
| #9 | (0,9) | 20 23 24 28 59 60 62 67 89 93 |
Birleştirme Sıralaması
Merge Sort: Zaman ve Alan Karmaşıklığı
an = a⌈n/2⌉ + a⌊n/2⌋ + n, a1 = 0
n = 2k: an = 2an/2 + n ⇒ an = Θ(n lg n)
- 16 öğe 8 + 8, 17 öğe 8 + 9 bölünür; merge n adım sürer.
- Çağrı sayısı Tn = 2Tn/2 + 1 ⇒ 2n − 1 (10 öğede 19 çağrı, 9'u merge yapar).
- Dizi durumundan bağımsız: en iyi = ortalama = en kötü = n lg n.
- Dezavantaj: O(n) ek bellek. Aşmak maliyeti artırır; ek bellek istenmiyorsa heap sort öne çıkar.
- Sıralı parçaları sırayla birleştirdiği için disk üzerindeki dış sıralamanın temelidir.
Klasik merge (eşitte soldakini alan) kararlıdır (quick ve heap sort kararlı değildir). Buradaki ters kopyalı (bitonik) merge kararlı değildir: [2a, 2b, 2c, 2d] → [2a, 2b, 2d, 2c] (Python ile denendi).
Birleştirme Sıralaması
Alfabetik Verileri Sıralamak: Bağlı Merge Sort
typedef struct { int anahtar; /* + büyük veri alanları */ int bag; /* sonraki öğenin indisi, 0 = son */ } eleman; int mergesort(eleman a[], int L, int R) { if (R <= L) return L; /* tek öğeli liste */ int m = (R + L) / 2; int sag = mergesort(a, m + 1, R); int sol = mergesort(a, L, m); return merge(a, sol, sag); /* baş indisi */ }
merge() iki sıralı listeyi bag alanları üzerinden birleştirip baş indisini döndürür.
- Büyük kayıtlar (ör. alfabetik bilgiler) kopyalanırsa zaman alıcıdır. Çözüm: bag alanıyla dizi içinde bağlı liste kurmak; yalnız indisler değişir.
- Veriler a[1..n]'dedir; a[0] merge sırasında geçici baş (takas alanı). Başta tüm bag alanları 0.
- mergesort, sıralanmış alt listenin baş indisini döndürür.
- Sık hatalar:
voidyordamdan değer döndürmek;elsedalında i2 yerine i1; a[0] yerinea[c].bagdöndürmek. - C'de argümanların değerlendirilme sırası belirsizdir; burada sıra açıkça yazıldı.
Birleştirme Sıralaması · Kodu adım adım çalıştır
Kod İzleme: Bağlı merge (#9)
merge #9: birinci liste (baş 4) 38→71→77→119→122; ikinci liste (baş 10) 13→22→41→43→115. sonuc = 0 (a[0] geçici baş).
38 > 13 → a[0].bag = 10; sonuc = 10; i2 = 7.
38 > 22 → a[10].bag = 7; sonuc = 7; i2 = 6.
38 ≤ 41 → a[7].bag = 4; sonuc = 4; i1 = 1.
71 > 41 → a[4].bag = 6; sonuc = 6; i2 = 9.
71 > 43 → a[6].bag = 9; sonuc = 9; i2 = 8.
71 ≤ 115 → a[9].bag = 1; sonuc = 1; i1 = 2.
77 ≤ 115 → a[1].bag = 2; sonuc = 2; i1 = 5.
119 > 115 → a[2].bag = 8; sonuc = 8; i2 = 0.
i2 = 0 (ikinci liste bitti) → a[8].bag = i1 = 5; baş a[0].bag = 10 döner: 13→22→38→41→43→71→77→115→119→122.
int merge(eleman a[], int birinci, int ikinci) { int sonuc = 0; // a[0]: geçici baş int i1 = birinci, i2 = ikinci; while (i1 && i2) if (a[i1].anahtar <= a[i2].anahtar) { a[sonuc].bag = i1; sonuc = i1; i1 = a[i1].bag; } else { a[sonuc].bag = i2; sonuc = i2; i2 = a[i2].bag; } a[sonuc].bag = i1 ? i1 : i2; return a[0].bag; }
Birleştirme Sıralaması
Bağlı Merge Sort: bag Alanlarının Değişimi
| # | [1] | [2] | [3] | [4] | [5] | [6] | [7] | [8] | [9] | [10] | dönüş | L, R |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| anahtar | 71 | 77 | 122 | 38 | 119 | 41 | 22 | 115 | 43 | 13 | ||
| #1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 9 | 10 | 9,10 |
| #2 | 0 | 0 | 0 | 0 | 0 | 0 | 6 | 0 | 0 | 9 | 7 | 6,7 |
| #3 | 0 | 0 | 0 | 0 | 0 | 8 | 6 | 0 | 0 | 9 | 7 | 6,8 |
| #4 | 0 | 0 | 0 | 0 | 0 | 9 | 6 | 0 | 8 | 7 | 10 | 6,10 |
| #5 | 0 | 0 | 0 | 5 | 0 | 9 | 6 | 0 | 8 | 7 | 4 | 4,5 |
| #6 | 2 | 0 | 0 | 5 | 0 | 9 | 6 | 0 | 8 | 7 | 1 | 1,2 |
| #7 | 2 | 3 | 0 | 5 | 0 | 9 | 6 | 0 | 8 | 7 | 1 | 1,3 |
| #8 | 2 | 5 | 0 | 1 | 3 | 9 | 6 | 0 | 8 | 7 | 4 | 1,5 |
| #9 | 2 | 8 | 0 | 6 | 3 | 9 | 4 | 5 | 1 | 7 | 10 | 1,10 |
Kalın: o merge'de değişen bag. Sıralı liste: 13 → 22 → 38 → 41 → 43 → 71 → 77 → 115 → 119 → 122.
Sınav Soruları
Sınav Sorusu
Aşağıdaki uyarlamayla eleman dizisi a[1..9] anahtara göre sıralanıyor. (i) Özyinelemeli çağrıların yığın çerçevelerini çizin; merge çağrılmışsa dönüş değerini yazın. (ii) İşletim tablosunu merge numaraları, dönüş değerleri ve her adımdaki bag alanlarıyla doldurun.
int mergesort(eleman a[], int L, int R) { int m; if (R <= L) return L; m = (R + L) / 2; return merge(a, mergesort(a, m + 1, R), mergesort(a, L, m)); }
Anahtarlar: [1] ?, [2] ?, [3] 18, [4] −5, [5] 27, [6] 11, [7] ?, [8] 83, [9] 30. Soru kâğıdındaki “?” yerine burada 42, 7, 55 alındı. merge() bağlı merge sort’taki gibidir; basılı sürümün else dalında i1 yazıyor (yazım hatası; doğrusu i2).
Sınav Soruları
Sınav Sorusu (3. soru): Çözüm
Çözüm
Argümanlar soldan sağa değerlendirilirse (önce sağ yarı) çağrı sırası: (1,9) → (6,9) → (8,9) → (9,9) → (8,8) → (6,7) → (7,7) → (6,6) → (1,5) …
| # | [1] | [2] | [3] | [4] | [5] | [6] | [7] | [8] | [9] | dön. | L,R,m |
|---|---|---|---|---|---|---|---|---|---|---|---|
| #1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 8 | 9 | 8,9,8 |
| #2 | 0 | 0 | 0 | 0 | 0 | 7 | 0 | 0 | 8 | 6 | 6,7,6 |
| #3 | 0 | 0 | 0 | 0 | 0 | 9 | 8 | 0 | 7 | 6 | 6,9,7 |
| #4 | 0 | 0 | 0 | 5 | 0 | 9 | 8 | 0 | 7 | 4 | 4,5,4 |
| #5 | 0 | 1 | 0 | 5 | 0 | 9 | 8 | 0 | 7 | 2 | 1,2,1 |
| #6 | 0 | 3 | 1 | 5 | 0 | 9 | 8 | 0 | 7 | 2 | 1,3,2 |
| #7 | 0 | 3 | 5 | 2 | 1 | 9 | 8 | 0 | 7 | 4 | 1,5,3 |
| #8 | 7 | 6 | 5 | 2 | 9 | 3 | 8 | 0 | 1 | 4 | 1,9,5 |
Sonuç listesi (baş 4): -5 → 7 → 11 → 18 → 27 → 30 → 42 → 55 → 83. Tablo simülasyonla üretildi.
Heap Sort
Heap Sort
void heapify(int a[], int kok, int n) { int t = a[kok], j; for (j = 2 * kok; j <= n; j *= 2) { if (j < n && a[j] < a[j + 1]) j++; // büyük if (t >= a[j]) break; // t buraya sığar a[j / 2] = a[j]; // çocuğu yukarı al } a[j / 2] = t; } void heapsort(int a[], int n) { /* a[1..n] */ int i; for (i = n / 2; i >= 1; i--) /* heap kur */ heapify(a, i, n); for (i = n - 1; i >= 1; i--) { /* sırala */ SWAP(a[1], a[i + 1]); /* en büyüğü sona */ heapify(a, 1, i); /* heap'i düzelt */ } }
- Karmaşıklık merge sort gibi O(n lg n); ama fazladan bellek kullanmaz (yerinde).
- Dizi a[1..n] (0 kullanılmaz) tam ikili ağaç gibi düşünülür: a[i]'nin çocukları a[2i], a[2i+1].
heapify(a, kok, n): kok'ün sol ve sağ alt ağaçları zaten max-heap iken kök değerine uygun yeri arar.- Yapraklar atlanır: ⌊n/2⌋, ⌊n/2⌋−1, …, 1 düğümleri için sondan başa heapify → max-heap.
- Sonra kökteki en büyük sona taşınır, kalan i öğe için heap düzeltilir.
forbaşlığında tanımlananjveidöngü dışında kullanılamaz (C99'da kapsam dışı).
Heap Sort · Kodu adım adım çalıştır
Kod İzleme: Sıralama Aşaması — SWAP + heapify(a, 1, i)
Girdi [26, 5, 77, 1, 61, 11, 59, 15, 48, 19] (Horowitz örneği), i = 5…1 için heapify ile aşağıdan yukarıya bu max-heap’e çevrildi (toplam 5 kaydırma). “Tamam hocam! Onu anladık da, bu dizi ne zaman sıralanacak?” — Şimdi: kökteki en büyük (77) her turda sona taşınır, kalan heap düzeltilir.
i = 9: SWAP(a[1], a[10]) → en büyük 77 sona; kökte 5 heap'i bozuyor.
heapify(a, 1, 9): 5 aşağı indi (a[8]); yeni kök 61 = kalanların en büyüğü.
i = 8: SWAP(a[1], a[9]) → en büyük 61 sona; kökte 1 heap'i bozuyor.
heapify(a, 1, 8): 1 aşağı indi (a[7]); yeni kök 59 = kalanların en büyüğü.
i = 7: SWAP(a[1], a[8]) → en büyük 59 sona; kökte 5 heap'i bozuyor.
heapify(a, 1, 7): 5 aşağı indi (a[5]); yeni kök 48 = kalanların en büyüğü.
i = 6: SWAP(a[1], a[7]) → en büyük 48 sona; kökte 1 heap'i bozuyor.
heapify(a, 1, 6): 1 aşağı indi (a[6]); yeni kök 26 = kalanların en büyüğü.
i = 5: SWAP(a[1], a[6]) → en büyük 26 sona; kökte 1 heap'i bozuyor.
heapify(a, 1, 5): 1 aşağı indi (a[4]); yeni kök 19 = kalanların en büyüğü.
i = 4: SWAP(a[1], a[5]) → en büyük 19 sona; kökte 5 heap'i bozuyor.
heapify(a, 1, 4): 5 aşağı indi (a[2]); yeni kök 15 = kalanların en büyüğü.
i = 3: SWAP(a[1], a[4]) → en büyük 15 sona; kökte 1 heap'i bozuyor.
heapify(a, 1, 3): 1 aşağı indi (a[3]); yeni kök 11 = kalanların en büyüğü.
i = 2: SWAP(a[1], a[3]) → en büyük 11 sona; kökte 1 heap'i bozuyor.
heapify(a, 1, 2): 1 aşağı indi (a[2]); yeni kök 5 = kalanların en büyüğü.
i = 1: SWAP(a[1], a[2]) → en büyük 5 sona; kökte kalan tek öğe (1) zaten bir heap'tir.
heapify(a, 1, 1): j = 2 > 1, döngüye girilmez; 1 yerinde kalır. Dizi sıralı: [1, 5, 11, 15, 19, 26, 48, 59, 61, 77].
void heapify(int a[], int kok, int n) { int t = a[kok], j; for (j = 2 * kok; j <= n; j *= 2) { if (j < n && a[j] < a[j + 1]) j++; // büyük if (t >= a[j]) break; // t buraya sığar a[j / 2] = a[j]; // çocuğu yukarı al } a[j / 2] = t; } for (i = n - 1; i >= 1; i--) { /* sırala */ SWAP(a[1], a[i + 1]); heapify(a, 1, i); }
Heap Sort
Heap Sort: Zaman Karmaşıklığı
- Kaba analiz: heapify en çok lg n kaydırma yapar, n/2 kez çağrılır → heap kurma n lg n.
- İnce analiz: k = düzey sayısı ≈ lg(n+1); i. düzeyde en çok 2i−1 düğüm, her biri en çok (k − i) kayar:
Σi=1k 2i−1(k − i) = 2k − k − 1 ≤ 2n = O(n)
- Sağdaki Σ i·2i−1 terimi, geometrik seri S(y) = (yk+1 − 1)/(y − 1)'in türevinde y = 2 konarak bulunur.
- Sıralama aşaması: n − 1 kez heapify(a, 1, i) → O(n lg n). Toplam O(n lg n), en kötü durumda da; ek bellek O(1); kararlı değil.
- “≤ 2n” sınırı O(n) demektir, o(n) değil (2n ile sınırlı olmak küçük-o değildir).
| k | Σ 2i−1(k−i) | 2k−k−1 | n = 2k−1 |
|---|---|---|---|
| 2 | 1 | 1 | 3 |
| 3 | 4 | 4 | 7 |
| 4 | 11 | 11 | 15 |
| 6 | 57 | 57 | 63 |
| 10 | 1013 | 1013 | 1023 |
Heap Sort
İleri Heapsort Algoritmaları ve Veriyi Tanımak
50.000'den az veri için heapsort makul bir algoritmadır (≈ 2n lg n karşılaştırma). Daha büyük veride karşılaştırma sayısını azaltan bottom-up heapsort ve kuramsal olarak daha da iyi sürümler vardır.
Kök boşluğu, t ile karşılaştırılmadan büyük çocuklar boyunca yaprağa kadar indirilir; sonra t yukarı doğru yerleştirilir. Düzey başına iki karşılaştırma yerine çoğunlukla bir.
Basamağa Göre Sıralama
Basamağa Göre Sıralama (Radix Sort)
- Birden çok anahtara göre sıralama: ör. kişileri önce soyadına, soyadı aynı olanları ada göre.
- Anahtarlar K0, K1, …, Kr−1: K0 en anlamlı (MSD), Kr−1 en az anlamlı (LSD). Örnekte soyadı en anlamlı.
- LSD radix sort: önce en az anlamlı basamağa göre, sonra sırayla daha anlamlılara göre; en son MSD'ye göre sıralanır.
- Her geçiş kararlı olmalı: önceki geçişin düzeni eşit basamaklarda korunur.
- Sayıları birbiriyle karşılaştırmaz → karşılaştırmasız (non-comparison) sıralama.
#define MAX_DIGIT 3 #define RADIX 10 typedef struct oge *oge_gost; struct oge { int deger[MAX_DIGIT]; /* 179 → {1, 7, 9} */ oge_gost bag; };
K0: yüzler (MSD) · K1: onlar · K2: birler (LSD). [0, 999] aralığı. typedef struct oge {…}; gibi tür adı verilmeyen bir typedef anlamsızdır.
Basamağa Göre Sıralama · Kodu adım adım çalıştır
Kod İzleme: radixsort — i = 2 (birler)
Geçiş i = 2 (birler basamağı): 10 kova boşaltıldı.
179: deger[2] = 9 → kova 9'un arkasına eklendi (geliş sırası korunur → kararlı).
208: deger[2] = 8 → kova 8'in arkasına eklendi (geliş sırası korunur → kararlı).
306: deger[2] = 6 → kova 6'nın arkasına eklendi (geliş sırası korunur → kararlı).
093: deger[2] = 3 → kova 3'ün arkasına eklendi (geliş sırası korunur → kararlı).
859: deger[2] = 9 → kova 9'un arkasına eklendi (geliş sırası korunur → kararlı).
984: deger[2] = 4 → kova 4'ün arkasına eklendi (geliş sırası korunur → kararlı).
055: deger[2] = 5 → kova 5'in arkasına eklendi (geliş sırası korunur → kararlı).
009: deger[2] = 9 → kova 9'un arkasına eklendi (geliş sırası korunur → kararlı).
271: deger[2] = 1 → kova 1'in arkasına eklendi (geliş sırası korunur → kararlı).
033: deger[2] = 3 → kova 3'ün arkasına eklendi (geliş sırası korunur → kararlı).
Toplama: kovalar 9'dan 0'a gezilip her kova listenin önüne bağlanır → küçük kova önde: 271 093 033 984 055 306 208 179 859 009.
for (int i = MAX_DIGIT - 1; i >= 0; i--) { for (int j = 0; j < RADIX; j++) on[j] = arka[j] = NULL; for (; p; p = p->bag) { // dağıt int b = p->deger[i]; if (!on[b]) on[b] = p; else arka[b]->bag = p; arka[b] = p; } for (int j = RADIX - 1; j >= 0; j--) // topla if (on[j]) { arka[j]->bag = p; p = on[j]; } } return p;
Basamağa Göre Sıralama · Kodu adım adım çalıştır
Kod İzleme (devam): i = 1 ve i = 0
Geçiş i = 1 (onlar basamağı): 10 kova boşaltıldı.
Dağıtım: her öğe onlar basamağına göre kovasının arkasına.
Toplama: kovalar 9'dan 0'a gezilip her kova listenin önüne bağlanır → küçük kova önde: 306 208 009 033 055 859 271 179 984 093.
Geçiş i = 0 (yüzler basamağı): 10 kova boşaltıldı.
Dağıtım: her öğe yüzler basamağına göre kovasının arkasına.
Toplama: kovalar 9'dan 0'a gezilip her kova listenin önüne bağlanır → küçük kova önde: 009 033 055 093 179 208 271 306 859 984.
Üç geçiş sonunda liste sıralı; baş işaretçisi döndürülür.
for (int i = MAX_DIGIT - 1; i >= 0; i--) { for (int j = 0; j < RADIX; j++) on[j] = arka[j] = NULL; for (; p; p = p->bag) { // dağıt int b = p->deger[i]; if (!on[b]) on[b] = p; else arka[b]->bag = p; arka[b] = p; } for (int j = RADIX - 1; j >= 0; j--) // topla if (on[j]) { arka[j]->bag = p; p = on[j]; } } return p;
Basamağa Göre Sıralama
Basamağa Göre Sıralama: Analiz
O(MAX_DIGIT · (RADIX + n + RADIX)) = O(MAX_DIGIT · (RADIX + n))
- Örnekte O(3 · (10 + n)): doğrusal görünür ama saymalı sıralama gibi özel bir durumdur: basamak sayısı ve taban sabit kabul edilmiştir.
- Veriler (a) pozitif, (b) tam sayı, (c) küçük aralıkta olmalı. Aralık büyürse yine sıralar ama karmaşıklık hızla artar.
- Ek bellek: 2·RADIX işaretçi (liste yerinde yeniden bağlanır).
| geçiş | liste |
|---|---|
| başlangıç | 179 208 306 093 859 984 055 009 271 033 |
| i = 2 | 271 093 033 984 055 306 208 179 859 009 |
| i = 1 | 306 208 009 033 055 859 271 179 984 093 |
| i = 0 | 009 033 055 093 179 208 271 306 859 984 |
Sınav Soruları
Sınav Sorusu: Değiştirilmiş Radix
Bir öğrenci, yordamın önce en anlamlı basamağı ele alması gerektiğini düşünüp dış döngüyü for (i = 0; i <= MAX_DIGIT-1; i++), toplama döngüsünü for (j = 0; j <= RADIX-1; j++) yapmıştır. Aşağıdaki liste için yordamı işletin, her adımda listeyi yazın ve sonucu yorumlayın.
p → 346 180 008 930 023 451 670 098 203 007
Çözüm
| geçiş | liste |
|---|---|
| i = 0 (yüzler) | 930 670 451 346 203 180 008 023 098 007 |
| i = 1 (onlar) | 098 180 670 451 346 930 023 203 008 007 |
| i = 2 (birler) | 098 008 007 346 023 203 451 180 670 930 |
1. değişiklik: birler basamağı en anlamlı basamak gibi davranır; sayılar ters okunursa sıralıdır (098, 008, 007 → 890, 800, 700 …). 2. değişiklik: kovalar ters sırada öne bağlandığı için sıra büyükten küçüğe döner. (Tablo simülasyonla üretildi.)
Laboratuvar · C kodu adım adım
Laboratuvar: İleri Sıralamalar
Özet
Karmaşıklık Özeti ve Karşılaştırma
| algoritma | en iyi | ortalama | en kötü | ek bellek | kararlı | yerinde |
|---|---|---|---|---|---|---|
| Seçimli | n² | n² | n² | O(1) | hayır | evet |
| Eklemeli | n | n² | n² | O(1) | evet | evet |
| Kabarcık (erken dur.) | n | n² | n² | O(1) | evet | evet |
| Shell (Knuth) | n lg n | — | n3/2 | O(1) | hayır | evet |
| Hızlı (quick) | n lg n | 1,39 n lg n | n² | O(lg n) yığın* | hayır | evet |
| Birleştirme (merge) | n lg n | n lg n | n lg n | O(n) | evet** | hayır |
| Heap sort | n lg n | n lg n | n lg n | O(1) | hayır | evet |
| Saymalı (counting) | n + k | n + k | n + k | O(n + k) | evet | hayır |
| Basamak (radix, d basamak) | d(n + r) | d(n + r) | d(n + r) | O(r)*** | evet | — |
* Küçük parça önce işlenirse; aksi hâlde en kötü O(n). ** Klasik merge ile. *** Bağlı liste üzerinde. Karşılaştırmalı sıralamada alt sınır ⌈lg n!⌉ ≈ n lg n − 1,44 n.
Özet
Hangi Durumda Hangisi?
Quick sort (medyan-3 + küçük parçada eklemeli) ya da kitaplık qsort.
Heap sort (yerinde) ya da introsort.
Merge sort: kararlı, sıralı parçaları birleştirir, dış sıralamanın temeli.
Kayıtlar değil indisler/bağlar sıralanır (bağlı merge sort).
Radix: d geçiş × O(n + r).
Saymalı sıralama.
Özet
Sık Yapılan Hatalar
j == L sınamasını unutmak: pivot en küçükse dizi başından taşar.b[]'yi her çağrıda malloc'lamak (yavaş) ya da hiç ayırmamak.return x - y;: taşma; ya da tutarsız (geçişsiz) karşılaştırıcı.Özet
Alıştırmalar
- [5, 9, 1, 7, 3] için
bol(a, 0, 4)sonucu diziyi ve dönüş değerini yazın. - Quick sort n = 8 sıralı dizide kaç bol() çağrısı yapar? Özyineleme derinliği nedir?
- Merge sort 16 öğe için kaç merge çağrısı yapar? En çok kaç karşılaştırma?
- [4, 10, 3, 5, 1] (a[1..5]) için heap kurma sonrası diziyi yazın.
- Radix sort 170, 45, 75, 90, 802, 24, 2, 66 listesinde birler geçişinden sonraki sırayı yazın.
- Heap sort neden kararlı değildir? Küçük bir örnek verin.
Cevapları göster
- v = 3. i = 0'da (5), j = 2'de (1) durur → takas: [1, 9, 5, 7, 3]; sonra i = 1 (9), j = 0 (1): çaprazlandı → a[1] ↔ a[4]: [1, 3, 5, 7, 9], dönüş 1.
- Pivot hep en büyük: boyut 8, 7, …, 2 için 7 bol() çağrısı; özyineleme derinliği n − 1 = 7.
- 15 merge çağrısı (n − 1). Bu merge her öğe için tam bir karşılaştırma yapar: lg 16 = 4 düzey × 16 = 64.
- [10, 5, 3, 4, 1].
- 170 090 802 002 024 045 075 066 (kararlı dağıtım).
- [2a, 2b, 1]: kök 2a sona taşınır → [1, 2b, 2a] gibi eşitlerin sırası değişebilir; uzak takaslar sırayı korumaz.
Özet
Bölüm Özeti
Pivotu yerine koyar ve problemi böler: en iyi n lg n, ortalama 1,39 n lg n, en kötü n².
Küçük parça önce (yığın lg n), medyan-3, küçük parçada eklemeli; qsort ve introsort.
Her durumda n lg n, O(n) ek bellek; bağlı sürüm büyük kayıtları kopyalamaz.
Heap kurma O(n), sıralama O(n lg n); yerinde ama kararsız.
Kararlı kovalarla LSD'den MSD'ye: O(d(n + r)); pozitif tam sayılar ve küçük aralık.
Yineleme bağıntıları, iterasyon, çıkarma hilesi, seri türevi — dersin bütün analizleri burada buluştu.
Özet
Kaynaklar
- R. Sedgewick, Algorithms in C, Kısım 1–4, Bölüm 7–10 (quicksort, mergesort, priority queues/heapsort, radix sorting).
- E. Horowitz, S. Sahni, S. Anderson-Freed, Fundamentals of Data Structures in C, Bölüm 7.
- T. H. Cormen vd., Introduction to Algorithms, Bölüm 6–8; D. Musser, “Introspective Sorting and Selection Algorithms”, 1997.
- ISO/IEC 9899 (C standardı),
qsorttanımı (§7.22.5.2).
Son
“Ustada kalırsa bu öksüz yapı, / Onu sürdürmeyen çırak utansın!” — N. F. K.
Dersin sonu · Ders sayfası