Bölüm 20: İleri Sıralama Algoritmaları
Çoktan Seçmeli · Hızlı Sıralama
Soru 1: bol Sonucu
a = [6, 2, 9, 1, 8, 5] için bol(a, 0, 5) çağrılıyor. Dizi ne olur, hangi değer döner?
A: Pivot v = 5. i 6'da (konum 0), j 1'de (konum 3) durur → takas: [1, 2, 9, 6, 8, 5]. Sonra i 9'da (2), j 2'de (1) durur; indisler çaprazlandı. Pivot a[2] ile takaslanır: [1, 2, 5, 6, 8, 9], dönüş 2. Solu ≤ 5 ≤ sağı; bu girdide sağ taraf da tesadüfen sıralı kaldı.
Adım Adım Çalıştırma · Hızlı Sıralama
Soru 2: Pivota Eşit Öğeler
a = [3, 8, 3, 1, 3] için bol(a, 0, 4) izleniyor. Pivota eşit öğelerde taramalar ne yapar, dönüş değeri nedir?
Pivot v = a[4] = 3. Dizide pivota eşit iki öğe daha var.
a[0] = 3: 3 < 3 yanlış → i = 0'da durur. Eşit öğe de i'yi durdurur.
a[3] = 1: 3 < 1 yanlış → j = 3'te durur.
i < j → takas: [1, 8, 3, 3, 3].
i 8'de (1) durur; j eşit öğe 3'te (2) durur: 3 < 3 yanlış.
Takas: [1, 3, 8, 3, 3]. Eşit bir 3 sola geçti.
i 8'de (2), j 3'te (1) durur: i ≥ j, döngüden çıkılır.
Pivot a[2]'ye: [1, 3, 3, 3, 8], dönüş 2. Eşitler iki tarafa dağıldı ve bölme ortaya düştü; eşitlerde durmak bu yüzden yararlıdır.
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; }
Çoktan Seçmeli · Hızlı Sıralama
Soru 3: Hepsi Eşit
1024 öğenin hepsi eşittir. quicksort1 için hangisi doğrudur?
C: kucuk(a[++i], v) ve kucuk(v, a[--j]) eşitlerde yanlıştır; iki tarama her öğede durur, takas yapar ve ortada buluşur. Sayaçlı programda 9218 karşılaştırma ve 11 düzey derinlik çıktı; sıralı 1024 öğede ise 525 822 karşılaştırma ve 1024 düzey.
Klasik Soru · Hızlı Sıralama
Soru 4: Sıralı Girdide Sayım
quicksort1 sıralı [1, 2, 3, 4, 5, 6] ile çağrılıyor. Kaç bol çağrısı ve bol içinde kaç karşılaştırma yapılır? Toplam kaç quicksort1 çağrısı olur, çağrı yığınında aynı anda en çok kaç quicksort1 çerçevesi bulunur?
Cevabı göster
- Pivot hep parçanın en büyüğüdür ve yerinde kalır; boyutlar 6, 5, 4, 3, 2 için 5
bolçağrısı. - m öğelik parçada i taraması m karşılaştırmada a[R]'ye kadar gider, j taraması ilk karşılaştırmada durur: m + 1. Toplam 7 + 6 + 5 + 4 + 3 = 25.
- Her
boliki çağrı doğurur: 1 + 2·5 = 11 çağrı (sağdakilerin hepsi boş). - Sol çağrılar 6, 5, 4, 3, 2, 1 öğeyle iç içe açılır: en çok 6 çerçeve.
Genelde ≈ n²/2 karşılaştırma ve n derinlik: büyük n'de yığın taşabilir (sayaçlı programla doğrulandı).
Hata Bulma · Hızlı Sıralama
Soru 5: j == L Sınaması
Bu bol sürümünde j döngüsündeki sınama silinmiştir. a = [5, 7, 6, 1] için bol(a, 0, 3) ne yapar? Neden i döngüsünde aynı sınama gerekmez?
int bol(int a[], int L, int R) { int i = L - 1, j = R, v = a[R]; for (;;) { while (kucuk(a[++i], v)) ; while (kucuk(v, a[--j])) ; if (i >= j) break; SWAP(a[i], a[j]); } SWAP(a[i], a[R]); return i; }
Cevabı göster
Pivot v = 1, dizinin en küçüğü. i, a[0] = 5'te durur. j ise 6, 7, 5'i geçer (hepsi 1'den büyük) ve a[--j] ile a[−1]'i okur: dizi dışına erişim, tanımsız davranış. Oradaki rastgele değer taramayı durdurana dek okumaya devam edilebilir, takas dizi dışına da yazabilir.
i taraması korumasızdır ama güvenlidir: en geç i = R'de a[R] = v ile kucuk(v, v) yanlış olur ve durur. Sol uçta böyle bir bekçi yoktur; if (j == L) break; gerekir. Medyan-3 sürümünde a[L] ≤ pivot olduğundan bekçi doğal olarak bulunur.
Adım Adım Çalıştırma · Hızlı Sıralamayı İyileştirmek
Soru 6: Açık Yığınla Quick Sort
quicksort2(a, 0, 6), a = [3, 7, 1, 6, 2, 5, 4]. Hangi (L, R) çiftleri hangi sırayla itilip alınır? Yığında en çok kaç çift bekler?
İlk problem (0, 6) itildi: önce R = 6, sonra L = 0; üstte L.
(0,6) alındı: bol → pivot 4 konum 3. Parçalar eşit (3 ve 3): önce sağ (4,6), sonra sol (0,2) itildi; küçük ya da eşit parça üstte.
(0,2): pivot a[2] = 1 en küçük, konum 0'a gider; dizi [1, 2, 3, …]. Sağ (1,2) büyük → önce o, sonra boş sol (0,−1) itildi.
(0,−1) alındı: boş aralık → continue.
(1,2): pivot 3 konum 2. Sol (1,1) büyük → önce o, sonra boş sağ (3,2).
(3,2) alındı: boş aralık → continue.
(1,1) tek öğe → continue. Sol yarı a[0..3] bitti.
(4,6): bol → [.., 5, 6, 7], pivot 6 konum 5. Eşit parçalar: (6,6) sonra (4,4) itildi.
(4,4) tek öğe → continue.
(6,6) de atlandı; yığın boş: [1, 2, 3, 4, 5, 6, 7]. Yığında aynı anda en çok 3 (L, R) çifti (6 int) 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 önce */ yigita_at(L, i - 1); yigita_at(i + 1, R); } else { yigita_at(i + 1, R); yigita_at(L, i - 1); } } }
Çoktan Seçmeli · Hızlı Sıralamayı İyileştirmek
Soru 7: Küçük Parça Önce İtilirse
quicksort2'de dal koşulu ters çevrilip küçük parça önce (alta), büyük parça sonra (üste) itilirse sıralı 1000 öğede ne olur?
B: Sıralı girdide her bölme boş bir parça ve n − 1 öğelik bir parça verir. Büyük parça üstte kalıp hemen işlenince boş parçalar alta birikir: her düzeyde bir çift. Sayaçlı denemede yığın 2000 int'e çıktı (doğru sürümde 4); YMAX = 64 ile UBSan dizi sınırı hatası verdi. Küçük parçayı önce işlemek derinliği O(lg n)'de tutar.
Çoktan Seçmeli · C Kitaplığı ve Hibrit
Soru 8: Farkla Karşılaştırma
qsort için karşılaştırıcı aşağıdaki gibi yazılmıştır. Dizide INT_MIN ve 1 bulunuyorsa ne olur?
int fark(const void *p, const void *q) { return *(const int *)p - *(const int *)q; } /* qsort(a, n, sizeof a[0], fark); */
D: İşaretli taşma tanımsızdır; pratikte sonuç pozitif büyük bir sayıya sarar ve INT_MIN, 1'den büyük sayılır. zig cc'nin UBSan'ı bu satırda "signed integer overflow" ile durdu. Güvenli kalıp (x > y) - (x < y)'dir. B yanlıştır: çıkarma, int'e dönüştürülmüş değerlerle yapılır.
Kod Yazma · C Kitaplığı ve Hibrit
Soru 9: İki Anahtarlı Karşılaştırıcı
typedef struct { char ad[12]; int not_; } ogrenci; dizisini qsort ile önce nota göre azalan, eşit notlarda ada göre alfabetik sıralayan int nota_sonra_ada(const void *p, const void *q) karşılaştırıcısını yazın. (Can 85), (Ali 70), (Banu 85), (Ece 90), (Deniz 70) için sonuç nedir?
Cevabı göster
int nota_sonra_ada(const void *p, const void *q) { const ogrenci *x = p, *y = q; if (x->not_ != y->not_) /* birincil: azalan */ return (y->not_ > x->not_) - (y->not_ < x->not_); return strcmp(x->ad, y->ad); /* ikincil: artan */ }
Sonuç: Ece 90, Banu 85, Can 85, Ali 70, Deniz 70.
qsort kararlı değildir; eşitlerin sırası önemliyse ikincil anahtar karşılaştırıcıya eklenir. void * → const ogrenci * dönüşümü C'de kendiliğinden yapılır; taşma riski olan çıkarma kullanılmadı.
Çoktan Seçmeli · C Kitaplığı ve Hibrit
Soru 10: Introsort
Introsort hızlı sıralamayla başlar. Özyineleme derinliği ≈ 2⌊lg n⌋'i aşan bir parçada ne yapar?
C: Derinlik sınırı aşıldığında parça heap sort ile sıralanır; heap sort yerinde ve en kötü durumda O(n lg n) olduğundan toplam en kötü durum O(n lg n) kalır. Eklemeli sıralama yalnız küçük parçalarda kullanılır (A büyük parçada O(n²) olurdu); birleştirme ek bellek ister.
Adım Adım Çalıştırma · Birleştirme Sıralaması
Soru 11: Ters Kopyalı merge
a = [2, 6, 8, 9, 1, 5, 7] için merge(a, b, 0, 3, 6) çağrılıyor. b nasıl doldurulur, i ve j her adımda nereyi gösterir?
Sol yarı a[0..3] = 2 6 8 9, sağ yarı a[4..6] = 1 5 7; ikisi de sıralı.
Sol yarı b[0..3]'e olduğu gibi kopyalandı; döngü i = L = 0 ile çıkar.
Sağ yarı b[4..6]'ya ters kopyalandı: b iki uçtan ortaya doğru artar. j = R = 6.
b[j] = 1 < b[i] = 2 → a[0] = 1; j--.
b[j] = 5 ≥ b[i] = 2 → a[1] = 2; i++.
b[j] = 5 < b[i] = 6 → a[2] = 5; j--.
b[j] = 7 ≥ b[i] = 6 → a[3] = 6; i++.
b[j] = 7 < b[i] = 8 → a[4] = 7; j--.
b[j] = 9 ≥ b[i] = 8 → a[5] = 8; i++.
b[j] = 9 ≥ b[i] = 9 → a[6] = 9; i++. Son adımda i = 4 ve j = 3 ortada buluştu; en büyük öğe 9 olduğu için sınır sınaması gerekmedi.
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++]; }
Çoktan Seçmeli · Birleştirme Sıralaması
Soru 12: 12 Öğede Sayım
mergesort(a, b, 0, 11) 12 öğe için kaç mergesort çağrısı, kaç merge yapar? Bu merge her öğe için bir karşılaştırma yaptığından toplam karşılaştırma kaçtır?
A: Çağrı sayısı 2n − 1 = 23, merge sayısı n − 1 = 11. Her merge R − L + 1 karşılaştırma yapar: 12 → 6 + 6 → 3 + 3 + 3 + 3 → her 3, 2 + 1'e bölünür. Düzeyler 12 + 12 + 12 + 8 (dört ikili birleşim) = 44 (sayaçla doğrulandı).
Kod Yazma · Birleştirme Sıralaması
Soru 13: Kararlı merge
Ters kopyalı (bitonik) merge kararlı değildir. a[L..m] ve a[m+1..R] sıralı iken bunları geçici bir dizide soldan sağa birleştiren, eşitlikte soldaki öğeyi alan int merge_kararli(int a[], int L, int m, int R) yordamını yazın. Geçici dizi heap'te ayrılsın.
Cevabı göster
int merge_kararli(int a[], int L, int m, int R) { int n = R - L + 1; int *t = malloc(n * sizeof *t); if (t == NULL) return -1; int i = L, j = m + 1, k = 0; while (i <= m && j <= R) t[k++] = (a[j] < a[i]) ? a[j++] : a[i++]; while (i <= m) t[k++] = a[i++]; /* kalanlar */ while (j <= R) t[k++] = a[j++]; memcpy(a + L, t, n * sizeof *t); free(t); return 0; }
Sağdaki öğe yalnız kesin küçükse alınır; eşitlikte soldaki önce gelir ve göreli sıra korunur. Bitonik sürümün aksine yarılardan biri bitince kalanlar ayrı döngülerle kopyalanır. Her çağrıda malloc yerine b dizisini bir kez ayırıp parametre olarak geçirmek daha hızlıdır.
Çoktan Seçmeli · Heap Sort
Soru 14: Heap Kurma
a[1..7] = [2, 9, 4, 7, 1, 8, 3] dizisine aşağıdan yukarıya heap kurma (i = 3, 2, 1 için heapify) uygulanıyor. Sonuç nedir?
D: i = 3: 4'ün çocukları 8 ve 3 → 8 yukarı: [2, 9, 8, 7, 1, 4, 3]. i = 2: 9 çocuklarından (7, 1) büyük, yerinde. i = 1: 2 aşağı iner: 9 yukarı, ardından a[2]'nin çocuklarından 7 yukarı, 2 a[4]'e: [9, 7, 8, 2, 1, 4, 3]. Max-heap sıralı dizi değildir (A).
Adım Adım Çalıştırma · Heap Sort
Soru 15: İki Kez En Büyüğü Sona
Max-heap a[1..7] = [9, 7, 8, 2, 1, 4, 3] ile sıralama aşamasının ilk iki turu (i = 6 ve i = 5) izleniyor. Her heapify sonunda dizi nedir?
Max-heap hazır. i = 6: SWAP(a[1], a[7]) en büyük 9'u sona taşıyacak.
9 yerine oturdu (yeşil). Kökte 3 heap'i bozuyor; heapify(a, 1, 6).
t = 3. Çocuklar 7 ve 8 → j = 3. 3 < 8: 8 yukarı alınır (a[1] = 8).
j = 6: tek çocuk (j < n yanlış). 3 < 4: 4 yukarı (a[3] = 4).
j = 12 > 6: döngü biter, a[6] = 3. Yeni kök 8 = kalanların en büyüğü.
i = 5: SWAP(a[1], a[6]) → 8 sona. heapify(a, 1, 5).
Çocuklar 7 ve 4 → j = 2. 3 < 7: 7 yukarı.
j = 4: çocuklar 2 ve 1, büyüğü 2. 3 ≥ 2: break, a[2] = 3. Dizi [7, 3, 4, 2, 1, 8, 9].
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++; if (t >= a[j]) break; a[j / 2] = a[j]; } a[j / 2] = t; } /* tur i: SWAP(a[1], a[i + 1]); heapify(a, 1, i); */
Klasik Soru · Basamağa Göre Sıralama
Soru 16: Basamak Geçişleri
LSD basamak sıralaması (kovaların arkasına ekleme, kovalar 0'dan 9'a toplanır) 329, 457, 657, 839, 436, 720, 355 listesine uygulanıyor. Her geçişten sonra liste nedir? Neden en az anlamlı basamaktan başlanır?
Cevabı göster
| geçiş | liste |
|---|---|
| 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 |
Birlerde 457 ve 657 aynı kovaya (7) düşer ve geliş sırası korunur. Onlarda 329 (2) 720 (2)'nin arkasına eklenir. Son geçiş en anlamlı basamağa göre sıralarken, eşit yüzler basamağında önceki geçişlerin düzeni (kararlılık sayesinde) korunur; bu yüzden sonuç tümüyle sıralıdır. 3 geçiş × O(n + 10).
Çoktan Seçmeli · Basamağa Göre Sıralama
Soru 17: Kovanın Önüne Eklemek
LSD basamak sıralamasında 329, 457, 657, 839, 436, 720, 355 listesi dağıtılırken her öğe kovasının arkası yerine önüne eklenirse (toplama değişmeden) üç geçişten sonra liste ne olur?
B: Öne eklemek her kovadaki geliş sırasını ters çevirir: geçişler kararsız olur. Son geçişte yüzler basamağı eşit olanlar (329 ile 355, 436 ile 457) ters sırada kalır (simülasyonla bulundu). D birler geçişinden sonraki ara listedir. LSD sıralama yalnız kararlı geçişlerle doğrudur.
Klasik Soru · Sınav Soruları
Soru 18: Bağlı merge ile Sıralama
Bağlı (indisli) birleştirme sıralaması a[1..4] anahtarları 40, 10, 30, 20 ile çağrılıyor; başta bütün bag alanları 0, a[0] geçici baş. mergesort(a, 1, 4) önce sağ yarıyı, sonra sol yarıyı sıralar. Her merge'den sonra bag alanları ve dönüş değerleri nedir?
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; }
Cevabı göster
| # | (L, R) | [1] | [2] | [3] | [4] | dönüş |
|---|---|---|---|---|---|---|
| 1 | (3, 4) | 0 | 0 | 0 | 3 | 4 |
| 2 | (1, 2) | 0 | 1 | 0 | 3 | 2 |
| 3 | (1, 4) | 0 | 4 | 1 | 3 | 2 |
- #1: 30 > 20, önce 4 (20) bağlanır, ikinci liste biter, a[4].bag = 3: 20 → 30.
- #2: 40 > 10: 10 → 40, baş 2.
- #3: 10 ≤ 20 → 2; 40 > 20 → a[2].bag = 4; 40 > 30 → a[4].bag = 3; ikinci liste bitti, a[3].bag = 1.
Baş 2: 10 → 20 → 30 → 40. Tutanaklar yerinden oynamadı, yalnız bag alanları değişti (C ile doğrulandı).
Çoktan Seçmeli · Özet
Soru 19: Hangi Algoritma?
10⁶ tutanak bir bağlı listede duruyor. Eşit anahtarlıların sırası korunmalı ve en kötü durumda da O(n lg n) isteniyor. Hangisi en uygundur?
C: Birleştirme sıralaması her durumda n lg n'dir, klasik merge ile kararlıdır ve bağlı listede yalnız bağları değiştirerek çalışır; dizideki O(n) ek bellek derdi de kalkar. Quick sort ve heap sort kararlı değildir, ikisi de dizide rastgele erişime dayanır; quick sort'un en kötüsü n²'dir. Radix ise anahtarların sabit uzunluklu tamsayı olmasını ister.