Bölüm 20: İleri Sıralama Algoritmaları

C ile Veri Yapıları ve Algoritmalar · Çalışma Soruları
Hızlı sıralamaİyileştirmelerC kitaplığı ve hibritBirleştirmeHeap sortBasamağa göre
Sercan KÜLCÜ

Ç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?

3081321334

Pivot v = a[4] = 3. Dizide pivota eşit iki öğe daha var.

3081321334i

a[0] = 3: 3 < 3 yanlış → i = 0'da durur. Eşit öğe de i'yi durdurur.

3081321334ij

a[3] = 1: 3 < 1 yanlış → j = 3'te durur.

1081323334ij

i < j → takas: [1, 8, 3, 3, 3].

1081323334ij

i 8'de (1) durur; j eşit öğe 3'te (2) durur: 3 < 3 yanlış.

1031823334ij

Takas: [1, 3, 8, 3, 3]. Eşit bir 3 sola geçti.

1031823334ij

i 8'de (2), j 3'te (1) durur: i ≥ j, döngüden çıkılır.

1031323384i

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 bol iki ç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?

30711263245546(0,6)yığın

İlk problem (0, 6) itildi: önce R = 6, sonra L = 0; üstte L.

30211243745566(4,6)(0,2)yığın

(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.

10213243745566(4,6)(1,2)(0,-1)yığın

(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.

10213243745566(4,6)(1,2)yığın

(0,−1) alındı: boş aralık → continue.

10213243745566(4,6)(1,1)(3,2)yığın

(1,2): pivot 3 konum 2. Sol (1,1) büyük → önce o, sonra boş sağ (3,2).

10213243745566(4,6)(1,1)yığın

(3,2) alındı: boş aralık → continue.

10213243745566(4,6)yığın

(1,1) tek öğe → continue. Sol yarı a[0..3] bitti.

10213243546576(6,6)(4,4)yığın

(4,6): bol → [.., 5, 6, 7], pivot 6 konum 5. Eşit parçalar: (6,6) sonra (4,4) itildi.

10213243546576(6,6)yığın

(4,4) tek öğe → continue.

10213243546576boşyığın

(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?

a20618293145576b_0_1_2_3_4_5_6

Sol yarı a[0..3] = 2 6 8 9, sağ yarı a[4..6] = 1 5 7; ikisi de sıralı.

a20618293145576b20618293_4_5_6i

Sol yarı b[0..3]'e olduğu gibi kopyalandı; döngü i = L = 0 ile çıkar.

a20618293145576b20618293745516ij

Sağ yarı b[4..6]'ya ters kopyalandı: b iki uçtan ortaya doğru artar. j = R = 6.

a10618293145576kb20618293745516ij

b[j] = 1 < b[i] = 2 → a[0] = 1; j--.

a10218293145576kb20618293745516ij

b[j] = 5 ≥ b[i] = 2 → a[1] = 2; i++.

a10215293145576kb20618293745516ij

b[j] = 5 < b[i] = 6 → a[2] = 5; j--.

a10215263145576kb20618293745516ij

b[j] = 7 ≥ b[i] = 6 → a[3] = 6; i++.

a10215263745576kb20618293745516ij

b[j] = 7 < b[i] = 8 → a[4] = 7; j--.

a10215263748576kb20618293745516i,j

b[j] = 9 ≥ b[i] = 8 → a[5] = 8; i++.

a10215263748596kb20618293745516ij

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?

978214391728324154637

Max-heap hazır. i = 6: SWAP(a[1], a[7]) en büyük 9'u sona taşıyacak.

378214931728324154697

9 yerine oturdu (yeşil). Kökte 3 heap'i bozuyor; heapify(a, 1, 6).

878214981728324154697

t = 3. Çocuklar 7 ve 8 → j = 3. 3 < 8: 8 yukarı alınır (a[1] = 8).

874214981724324154697

j = 6: tek çocuk (j < n yanlış). 3 < 4: 4 yukarı (a[3] = 4).

874213981724324153697

j = 12 > 6: döngü biter, a[6] = 3. Yeni kök 8 = kalanların en büyüğü.

374218931724324158697

i = 5: SWAP(a[1], a[6]) → 8 sona. heapify(a, 1, 5).

774218971724324158697

Çocuklar 7 ve 4 → j = 2. 3 < 7: 7 yukarı.

734218971324324158697

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
birler720 355 436 457 657 329 839
onlar720 329 436 839 355 457 657
yüzler329 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)00034
2(1, 2)01032
3(1, 4)04132
  • #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.

Son

Bölüm 20: İleri Sıralama Algoritmaları — Çalışma Soruları
Konu anlatımı: İleri sıralama algoritmaları sunumu · Ders notu · Terim sözlüğü
1 / 1 Sercan KÜLCÜ, Tüm hakları saklıdır.