8  Doğrusal Zamanlı Sıralama

Karşılaştırma alt sınırı Ω(n log n), direct access array sort, tuple/Excel sort, kararlı sıralama, counting sort ve radix sort O(n)

NotBölüm bilgisi

8.1 Bu Derste Ne Var?

Ders 4’te merge sort, bir diziyi \(O(n \log n)\)’de sıraladı. Bu ders iki soru sorar: (1) karşılaştırma modelinde daha hızlı olabilir miyiz — hayır, \(n \log n\) optimaldir; (2) modeli genişletirsek — evet, anahtarlar küçük tam sayıysa \(O(n)\) sıralama mümkündür.

Üç temel kavram bu derste yan yana gelir:

  1. Sıralama alt sınırı — karşılaştırma modelinde her sıralama \(\Omega(n \log n)\)’dir (\(n!\) yaprak → karar ağacı argümanı).
  2. Direct access array sort → counting sort — anahtarı doğrudan indeks yap; çakışmaları zincirle, geliş sırasını koru.
  3. Radix sort — büyük anahtarları base-\(n\) basamaklara ayır, kararlı (stable) sıralamayla basamak basamak sırala.

“n log n is optimal.” — Ku, 10:19

flowchart TD
    A["Ders 7: Doğrusal Zamanlı Sıralama"] --> L["Karşılaştırma alt sınırı<br/>Ω(n log n) · n! yaprak"]
    A --> M["Modeli değiştir<br/>karşılaştırma → indeks"]
    L --> L1["Karar ağacı<br/>log(n!) = Θ(n log n)"]
    M --> D["Direct access array sort<br/>benzersiz anahtar · O(n + u)"]
    D --> T["Anahtarı base-n böl<br/>k = a·n + b"]
    T --> TS["Tuple / Excel sort<br/>en az → en önemli basamak"]
    TS --> ST["Kararlı (stable) sort<br/>eşit anahtar girdi sırasını korur"]
    ST --> C["Counting sort<br/>zincir + geliş sırası · O(n + u)"]
    C --> R["Radix sort<br/>log_n u basamak · kararlı counting"]
    R --> O["u ≤ n^c  →  O(n)<br/>DOĞRUSAL"]

    classDef root fill:#fef3c7,stroke:#b45309,stroke-width:3px,color:#1f2937
    classDef branch fill:#f3f4f6,stroke:#374151,stroke-width:2px,color:#1f2937
    classDef leaf fill:#ffffff,stroke:#9ca3af,stroke-width:1px,color:#1f2937
    class A root
    class L,M,D,C,R branch
    class L1,T,TS,ST,O leaf
Şekil 8.1: Ders 7’nin kavram haritası: karşılaştırma modelinin Ω(n log n) alt sınırından (n! yaprak → karar ağacı) modeli değiştirmeye — direct access array sort (benzersiz anahtar) → counting sort (zincir + geliş sırası, kararlı) → radix sort (base-n basamak + kararlı counting sort) → u ≤ n^c koşulunda O(n) doğrusal sıralama.
İpucuBuilder Notu — Counting Sort = Hash’in Sıra-Koruyan İkizi

Bu dersin çekirdeği, Ders 5’teki (Hashing) “anahtar = indeks” fikrinin arama yerine sıra için yeniden kullanılmasıdır:

  • Geriye → Ders 5 (Hashing): Counting sort’un “her indekste bir zincir” yapısı, hash tablosunun çakışma çözümüyle (chaining) birebir aynı veri yapısıdır — fark, amacın arama değil sıra korumak olmasıdır. Hash’te zincir sırası önemsizdi; burada zincire geldikleri sırada ekleme, kararlılığın (stability) ta kendisidir.
  • Geriye → PS1 (Stirling): \(\log(n!) = \Theta(n \log n)\) alt sınırı, problem setindeki Stirling yaklaşımının doğrudan uygulamasıdır; \(\log(10^9!) \approx\) milyarlarca karşılaştırma mertebesi.
  • İleriye → büyük veri: radix sort, tam sayı / sabit-uzunluk anahtarlarda (IP adresleri, timestamp, sabit ID) karşılaştırmalı sıralamayı geçer; veritabanı ve GPU sıralamasında standart.
  • İleriye → çok-kolonlu sıralama: “Excel spreadsheet sort” — kararlı sıralamayı en az önemliden en önemliye uygulamak, SQL ORDER BY a, b ve pandas sort_values mantığının ta kendisidir.

Tek cümle: Karşılaştırma seni \(n \log n\)’de tutar; ama anahtarlar küçük tam sayıysa, onları doğrudan indeks yapıp basamak basamak kararlı sıralayarak \(O(n)\)’e inebilirsin.

8.2 Önceki Ders: Hash Tablosunun Sınırı

Hash tablosu find/insert/delete’i beklenen \(O(1)\) yapar — ama en kötü durumda \(O(n)\)’dir (kötü hash → tüm anahtarlar tek zincirde). Bu yüzden en-kötü-durum garantisi gerektiğinde (örneğin worst-case isteyen problem setlerinde) hash tablosu uygun değildir; sıralı dizi (\(O(\log n)\) garantili) tercih edilir. (Java, zincirleri ağaçla tutarak worst-case \(O(\log n)\) elde eder.) Bu ders aramayı değil, sıralamayı ele alır.

8.3 Sıralama Alt Sınırı: n log n

Ders 5’teki karar ağacı argümanını sıralamaya uygularız. Bir sıralama algoritmasının çıktısı, girdinin bir permütasyonudur: ilk öğe nereye, ikinci nereye, … Her öğe için bağımsız seçim → toplam \(n!\) olası çıktı.

“I need at least n factorial leaves.” — Ku, 13:55

Çalışılan Örnek — \(n \log n\) alt sınırı. Doğru bir sıralama, \(n!\) permütasyonun her birini üretebilmeli → karar ağacında en az \(n!\) yaprak. İkili ağacın minimum yüksekliği \(\log(n!)\). Bunu alttan sınırla: \(n!\) çarpımındaki \(n\) terimden yarısı (\(n/2\) terim) en az \(n/2\)’dir, dolayısıyla \(n! \ge (n/2)^{n/2}\). Logaritması:

\[\log(n!) \ge \frac{n}{2} \cdot \log\frac{n}{2} = \Theta(n \log n)\]

“any sorting algorithm here takes at least n log n comparisons, and so a merge sort’s the best we can do.” — Ku, 16:17

Demek ki karşılaştırma modelinde \(\Omega(n \log n)\) alt sınırı vardır; merge sort optimaldir. (PS1’deki Stirling yaklaşımı da aynı sonucu verir.) Daha hızlısı için karşılaştırmadan çıkıp rastgele erişim kullanmak gerekir. Şekil 8.2 bu argümanı küçük bir karar ağacı ve \(\log(n!)\) büyüme eğrisiyle birlikte gösterir.

Şekil 8.2: Karşılaştırma modelinde sıralamanın alt sınırı: doğru bir sıralama, girdideki \(n\) öğenin herhangi bir sıralanışını düzeltebilmeli — yani \(n!\) olası permütasyonun her birini üretebilmelidir. Algoritmayı bir karar ağacı olarak düşünün: her iç düğüm bir karşılaştırma (\(<\)), her yaprak nihai bir permütasyon. Sol: \(n=3\) için ağaç — \(3! = 6\) yaprak (amber) gerekir; ikili bir ağacın \(h\) yükseklikte en fazla \(2^h\) yaprağı olduğundan \(2^h \ge n!\), yani yükseklik \(\ge \lceil\log_2(3!)\rceil = 3\). Bu yükseklik en kötü durumdaki karşılaştırma sayısıdır. Sağ: \(\log_2(n!)\) (tam alt sınır, \(\sum_{i=1}^{n}\log_2 i\)) ile kaba alt sınır \((n/2)\log_2(n/2)\) ve \(n\log_2 n\) referansı aynı büyüme bandında ilerler — Stirling ile \(\log_2(n!) = n\log_2 n - n\log_2 e + \tfrac{1}{2}\log_2(2\pi n) = \Theta(n\log n)\). Sonuç: yalnız karşılaştırmaya dayanan hiçbir sıralama \(\Omega(n\log n)\)’den hızlı olamaz — bu, doğrusal zamanın önündeki \(n\log n\) duvarıdır. Duvarı yıkmanın tek yolu karşılaştırmayı bırakıp anahtarı doğrudan adres olarak kullanmaktır (counting / radix sort).
İpucuBuilder Notu — n log n Duvarı ve Model Değiştirme

\(\Omega(n \log n)\) bir algoritma sınırı değil, bir model sınırıdır — ve bu ayrım bir builder için kritiktir:

  • Sınır neye bağlı: Alt sınır “yalnız karşılaştırma yapan” algoritmalar için geçerlidir. İki öğeyi karşılaştırmak en çok 1 bit bilgi verir; \(n!\) olası çıktıyı ayırt etmek için en az \(\log_2(n!) = \Theta(n \log n)\) bit, yani o kadar karşılaştırma gerekir. Merge sort bu sınıra ulaştığı için karşılaştırma dünyasında optimaldir.
  • Tek çıkış = modeli değiştirmek: Daha hızlı olmak istiyorsan karşılaştırmayı bırakıp anahtarı doğrudan adres olarak kullanmalısın (rastgele erişim). Counting/radix sort tam da bunu yapar — bu yüzden alt sınırı ihlal etmezler, dışına çıkarlar.
  • Genel ders: Bir alt sınıra çarptığında soru “daha akıllı bir algoritma var mı?” değil, “varsayımlardan hangisini gevşetebilirim?” olur. Burada gevşetilen varsayım “anahtarlar yalnız karşılaştırılabilir”; yerine “anahtarlar küçük tam sayı” konur.

8.4 Karşılaştırmanın Ötesi: Direct Access Array Sort

Anahtarlar benzersiz (unique) ve küçük aralıkta ise: büyük bir direct access array kur, her öğeyi x.key indeksine koy (\(O(1)\)), sonra diziyi baştan sona gez ve dolu hücreleri sırayla topla.

“if u is on the order of n, then we now have linear time sorting algorithm.” — Ku, 21:56

def daa_sort(A, u):
    D = [None] * u
    for x in A:
        D[x.key] = x          # benzersiz anahtar -> cakisma yok
    out = []
    for slot in D:            # 0..u-1 sirayla gez
        if slot is not None:
            out.append(slot)
    return out

Karmaşıklık: diziyi kurmak + gezmek \(O(u)\), \(n\) öğe eklemek \(O(n)\) → toplam \(O(n + u)\). \(u = \Theta(n)\) ise lineer. Ama iki kısıt var: anahtarlar benzersiz ve aralık küçük olmalı.

8.5 Daha Büyük Aralık: Anahtarı Basamaklara Ayır

\(u \le n^2\) olsun. \(n^2\)-boyutlu bir dizi quadratik olur — işe yaramaz. Çözüm: her anahtarı base-\(n\) iki basamağa ayır. \(k\) için:

\[a = k // n, \quad b = k \bmod n, \quad k = a \cdot n + b\]

Burada \(a\) n’ler basamağı, \(b\) birler basamağıdır. Her basamak \(0..n-1\) aralığındadır. Örneğin \(n = 5\) için \(17 = (3, 2)\) çünkü \(3 \cdot 5 + 2 = 17\). Artık tam sayıları değil, bu ikililer (tuple) üzerinden sıralayacağız.

İpucuBuilder Notu — Böl ve Eşle (radix/bucket/hash ortak kök)

Anahtarı base-\(n\) basamaklara ayırmak, yalıtık bir hile değil — bilgisayar biliminin tekrar tekrar gördüğü bir kalıptır: büyük bir problemi sabit sayıda küçük parçaya böl, her parçayı bir kovaya eşle.

  • Radix: anahtarı basamaklara böl, her basamağı \(0..n-1\) kovasına eşle (bu ders).
  • Bucket sort: anahtar aralığını eşit kovalara böl, her öğeyi kovasına at, kova içini sırala — aynı “böl ve eşle” sezgisi, farklı bölme kuralı.
  • Hash bölmeleri (partitioning): dağıtık sistemlerde (Spark, veritabanı sharding) anahtarı hash(k) mod p ile \(p\) parçaya böl — her parça bağımsız işlenir.

Üçü de \(k = a \cdot n + b\) türünden bir ayrıştırmayla “tek büyük indeks” yerine “sabit sayıda küçük indeks” kullanır. Bir builder bu kalıbı tanırsa, “anahtar evreni çok büyük” probleminde refleks olarak basamaklara/kovaları bölmeyi dener.

8.6 Tuple Sort (Excel Tablo Sıralaması)

İkilileri sıralamak, bir Excel tablosunu kolonlara göre sıralamaya benzer: her kolonun bir öncelik sırası var. En önemli basamak (\(a\)), en az önemliden (\(b\)) daha belirleyicidir — \(a\)’yı 1 artırmak, \(b\) ne olursa olsun sayıyı büyütür.

“tuple sort, but you can also think of it as Excel spreadsheets sort.” — Ku, 29:14

Strateji: basamakları en az önemliden en önemliye doğru, art arda sırala. Ama bu yalnızca her geçişte kararlı (stable) bir sıralama kullanılırsa işe yarar — sonraki bölümün konusu. Şekil 8.3, \(17, 3, 24, 22, 12\) örneğinde iki kararlı geçişi yan yana gösterir.

Şekil 8.3: Tuple sort = Excel kolon sıralaması (Ku \(\S 5\)): bir sayıyı base-\(n\) basamaklarından oluşan bir ikili \((a, b)\) gibi düşün (\(a\) = \(n\)’ler, \(b\) = birler). \(17,3,24,22,12\) (\(n=5\), base-\(5\)) örneği iki kararlı geçişle sıralanır: önce EN AZ önemli basamağa (birler, amber kolon) göre kararlı sırala, sonra EN önemli basamağa (\(n\)’ler, amber kolon) göre kararlı sırala. Sonuç \(3,12,17,22,24\). Kararlılık şart: 3. geçişte \(n\)’ler basamağı eşit olan \(24=(4,4)\) ve \(22=(4,2)\) ikilileri, 1. geçişte kurulan birler sırasını korur (\(22\) önce, \(24\) sonra). Bu, radix sort’un counting sort’u neden kararlı yardımcı olarak kullandığının çekirdeğidir.

8.7 Kararlı Sıralama (Stable Sort)

Bir sıralama kararlıdır eğer eşit anahtarlı öğeler, çıktıda girdideki göreli sıralarını korur.

“if they are the same thing, then the output maintains their order from the input… that’s what we call a stable sorting algorithm.” — Ku, 36:18

Neden kritik? Tuple sort’ta önce en az önemli basamağı sıralarız; sonra en önemliyi. En önemli basamak eşit olan öğeler için, önceki (az önemli) sıralamanın korunması gerekir — bu yalnızca kararlı sıralamayla olur. Şekil 8.4, aynı girdiyi kararlı ve kararsız sıralayarak farkı görselleştirir.

Şekil 8.4: Kararlı (stable) sıralama: eşit anahtarlı ama farklı yüklü (payload) öğeler için, kararlı bir sıralama bunların girdideki göreli sırasını korur; kararsız bir sıralama onları yeniden dizebilir. Üstte girdi: anahtarlar \(3, 1, 3, 2, 1, 3\) (altlarındaki \(a..f\) etiketleri girdi sırasını işaretler — eşit anahtarlı öğeleri birbirinden ayıran yük). Ortada kararlı çıktı (amber ✓): anahtar \(= 3\) grubu \(a \to c \to f\) sırasını AYNEN korur, anahtar \(= 1\) grubu \(b \to e\) kalır. Altta kararsız çıktı (✗): anahtarlar yine artan (\(1,1,2,3,3,3\)) ama eşit anahtar içinde sıra bozulur (\(3\) grubu \(f \to c \to a\)). Anahtarlar sıralı olsa bile kararsız sonuç farklıdır — bu yüzden çok-kolonlu sıralama (önce ikincil, sonra birincil anahtar) ve radix sort yalnız kararlı bir alt-sıralama ile doğru çalışır.
İpucuBuilder Notu — Kararlılık ve Çok-Kolonlu Sıralama (SQL ORDER BY)

Kararlılık akademik bir incelik değil, her gün kullandığın çok-kolonlu sıralamanın temelidir:

  • SQL ORDER BY a, b: Bunu doğru gerçekleştirmenin bir yolu, önce ikincil anahtar b’ye göre kararlı sırala, sonra birincil anahtar a’ya göre kararlı sırala. a eşit olan satırlar b sırasını korur — tuple sort’un birebir aynısı. Anahtar her zaman az önemliden çok önemliye uygulanır.
  • pandas df.sort_values([...]) ve Excel’in çok-seviyeli sıralaması aynı mantığı kullanır; altta yatan sıralama kararlı olmasaydı ikincil sıra rastgele bozulurdu.
  • Programlama dili garantisi: Python’un sorted/list.sort ve Java’nın nesne sıralaması kararlı olmayı garanti eder (Timsort); C++ std::sort etmez (kararlı isterse std::stable_sort). Bir builder bu garantiyi bilmeden çok-kolonlu sıralama yazarsa, dilden dile sessizce farklı sonuçlar alır.

Tek cümle: Kararlılık, “önce ikincil sonra birincil anahtar” hilesiyle çok-kolonlu sıralamayı tek-kolonlu sıralamalardan kurmanı sağlar.

8.8 Counting Sort

Direct access array sort benzersiz anahtar isterdi; ama tuple sort’ta basamaklar tekrar eder. Çözüm: her indekste tek öğe yerine bir zincir (sequence veri yapısı — dinamik dizi/bağlı liste) tut ve öğeleri geldikleri sırada sona ekle. Bu counting sort’tur — hashing’e benzer ama amaç sıra korumak.

“This is called counting sort.” — Ku, 39:41

Okurken, dolu zincirleri \(0\)’dan \(u-1\)’e gez ve içlerini sırayla oku. Geliş sırası korunduğu için counting sort kararlıdır. Zincirlerin toplam uzunluğu \(n\), dizi boyutu \(u\)\(O(n + u)\). Şekil 8.5 bu zincir/histogram yapısını ve kararlılığı adım adım gösterir.

Şekil 8.5: Counting sort (Ku \(\S 7\), İMZA figür): doğrudan erişim dizisinin sıra-koruyan ikizi. \(u\) kova (burada \(u = 6\), indeks \(0..5\)); her öğenin anahtarı \(k\), onu \(k\). kovaya yollar. Doğrudan erişim sortundan farkı: her kovada tek öğe değil bir ZİNCİR (chain) tutulur ve öğeler geliş sırasıyla sona eklenir (amber yukarı-oklar geliş yönünü gösterir). Girdi \((3a, 1a, 3b, 5a, 1b, 3c)\) — harfler eşit anahtarların girdi sırasını işaretler. Dağıtımdan sonra zincirler sayma histogramıdır: kova \(1 = [1a, 1b]\) (#2), kova \(3 = [3a, 3b, 3c]\) (#3), kova \(5 = [5a]\) (#1), gerisi boş. Çıktıyı üretmek için zincirleri \(0\)’dan \(u-1\)’e sırayla oku: \([1a, 1b, 3a, 3b, 3c, 5a]\) — eşit anahtarlı öğeler girdi sırasını korur, yani sıralama kararlıdır (stable). Toplam iş = zincirlere \(n\) öğe ekle + \(u\) kovayı gez \(= O(n + u)\); \(u = \Theta(n)\) ise lineer. Bu kararlılık, radix sort’un (Şekil 8.6) basamak-basamak doğru çalışmasının temelidir.

8.9 Radix Sort

Counting sort’u tuple sort’un yardımcı (kararlı) sıralaması olarak kullanırsak, büyük anahtarları sıralayabiliriz. Radix sort: en büyük anahtar \(u\) olan tam sayıları base-\(n\) basamaklara ayır (\(\log_n u\) basamak), sonra counting sort ile en az önemliden en önemliye tuple sort yap.

“this is what we call radix sort.” — Ku, 47:39

Her basamağı sıralamak \(O(n)\) (counting sort, \(u = n\)). Basamak sayısı \(\log_n u\). Ayrıca her sayıyı ayırmak da \(O(n \cdot \log_n u)\). Toplam:

\[T(n) = O(n + n \cdot \log_n u)\]

Kritik sonuç: \(u \le n^c\) (sabit \(c\)) ise, \(\log_n u = \log_n n^c = c\) (sabit) → $T(n) = $ \(O(n)\).

“if u is less than n to the c for some constant c… we get a linear time algorithm.” — Ku, 50:35

Yani anahtarlar \(n\)’in bir polinomuyla sınırlıysa, radix sort lineer zamanda sıralar. Şekil 8.6, \(17, 3, 24, 22, 12\) üzerinde iki kararlı counting sort geçişini (LSD → MSD) gösterir.

Şekil 8.6: Radix sort = counting sort + base-\(n\) basamaklar (Ku §8, İMZA örneği \(17, 3, 24, 22, 12\); \(n=5\), taban \(5\) → her sayı iki basamak). \(u = \max + 1 = 25 \le n^2\) olduğu için \(\lceil \log_5 25 \rceil = 2\) basamak yeter. 1. geçiş (LSD, birler basamağı): girdideki her sayının en az önemli basamağı (amber rozet \([d_0]\): \(17{=}[2]\), \(3{=}[3]\), \(24{=}[4]\), \(22{=}[2]\), \(12{=}[2]\)) ile kararlı counting sort → \([17, 22, 12, 3, 24]\). 2. geçiş (MSD, n’ler basamağı): bu kez en önemli basamak (\([d_1]\): \(17{=}[3]\), \(22{=}[4]\), \(12{=}[2]\), \(3{=}[0]\), \(24{=}[4]\)) ile yine kararlı counting sort → \([3, 12, 17, 22, 24]\) (amber, sıralı ✓). Kararlılık şarttır: 2. geçişte n’ler basamağı eşit olan (\(22\) ve \(24\), ikisi de \(4\)) öğeler 1. geçişten gelen birler sırasını korur → tuple sort doğru çalışır. Her geçiş \(O(n + \text{base})\); sabit sayıda geçiş (\(u \le n^c\)\(c\) basamak), \(\text{base} = n\) alınırsa toplam \(O(n)\)doğrusal zaman, karşılaştırma sınırı \(\Omega(n \log n)\) aşılır.
İpucuBuilder Notu — Radix Sort, GPU ve Büyük Veri

Radix sort akademik bir oyuncak değil; sabit-uzunluk tam sayı anahtarlar söz konusu olduğunda gerçek dünyada karşılaştırmalı sıralamayı geçer:

  • Sabit-uzunluk anahtarlar her yerde: 32-bit IP adresleri, 64-bit timestamp’ler, sabit ID’ler — hepsi \(u \le n^c\) koşulunu pratikte sağlar (örn. \(u = 2^{32}\), \(n = 10^6\) için \(\log_n u \approx \log_{10^6} 2^{32} \approx 1.6\), yani 2 basamak yeter). Bu yüzden ağ/log işleme boru hatlarında radix sort doğal tercihtir.
  • GPU sıralama: Radix sort, karşılaştırma yerine basamak sayımına (histogram) dayandığından paralelleştirmesi kolaydır — her thread bir öğenin basamağını sayar, prefix-sum ile yerini bulur. CUDA Thrust ve cuDF gibi kütüphanelerin varsayılan tam sayı sıralaması radix tabanlıdır; merge sort’un dallanması GPU’da pahalıdır, radix’in düz histogramı değildir.
  • Veritabanı / büyük veri: Spark, ClickHouse gibi sistemler tam sayı/sabit anahtar kolonlarında radix veya hibrit (radix + merge) sıralama kullanır; “anahtar küçük tam sayı mı?” sorusu sıralama planlayıcısının ilk kontrolüdür.

Tek cümle: Anahtarların küçük tam sayı olduğunu bildiğin an, radix sort hem asimptotik (\(O(n)\)) hem donanım (GPU paralelliği) açısından merge sort’u geçer.

8.10 Bu Dersin Özeti

  1. Karşılaştırma modelinde sıralama \(\Omega(n \log n)\)’dir: \(n!\) permütasyon → \(n!\) yaprak → \(\log(n!) = \Theta(n \log n)\).
  2. Direct access array sort: benzersiz anahtar + küçük aralık → \(O(n + u)\); \(u = \Theta(n)\) ile lineer.
  3. Anahtarı base-\(n\) basamaklara ayır (\(a = k // n\), \(b = k \bmod n\)) → \(u \le n^2\) için ikililer.
  4. Tuple sort: basamakları en az önemliden en önemliye, kararlı sıralamayla sırala.
  5. Kararlı (stable) sıralama: eşit anahtarlar girdi sırasını korur — tuple sort için şart.
  6. Counting sort: direct access + zincir (sıra korur); kararlı; \(O(n + u)\).
  7. Radix sort: \(\log_n u\) basamak, counting sort ile; \(u \le n^c\) ise \(O(n)\).
ÖnemliTek Bir Cümle

Karşılaştırma \(n \log n\)’de tıkanır; ama anahtarlar küçük tam sayıysa, onları indeks yapıp (counting sort) basamak basamak kararlı sıralayarak (radix sort) \(O(n)\)’e inilir.

Şekil 8.7 bu sıçramayı tek grafikte özetler: Ders 4’ün \(n \log n\) merge sort’undan, bu dersin \(O(n)\) radix sort’una.

Şekil 8.7: Sıralamanın evrimi — maliyet duvarının yıkılışı. Slate eğri: merge sort, \(n \cdot \log_2 n\) (böl-yönet + two-finger merge, Ders 4/L3) — karşılaştırma modelindeki \(\Omega(n \log n)\) duvarı, selection sort’un \(\Theta(n^2)\)’sinden çok daha iyi olsa da bu duvarı aşamaz. Amber çizgi: radix sort, \(3 \cdot n = O(n)\) — anahtarlar küçük tamsayı olduğunda (\(u \le n^3\), yani \(c = 3\) sabit basamak) sıralama doğrusal. İki eğri \(n \approx 9\)’da kesişir (noktalı amber dikey çizgi); ötesinde radix sort merge sort’un altında kalır, yani doğrusal kazanır. Sıçramanın sebebi modeli değiştirmektir: karşılaştırma yerine anahtarı doğrudan indeks olarak kullanmak (counting/radix sort), karşılaştırma alt sınırının dışına çıkmanın tek yoludur. Bu figür Ders 4’ün \(n \log n\) merge sort’unu (bkz. Şekil 5.5) Ders 7’nin \(O(n)\) radix sort’una (bkz. Şekil 8.6) bağlar.

8.11 Kontrol Soruları

Cevap: Bir sıralama, girdinin \(n!\) olası permütasyonundan herhangi birini üretebilmeli → karar ağacında en az \(n!\) yaprak. İkili ağacın minimum yüksekliği \(\log(n!)\), ve en kötü durum karşılaştırma sayısı = yükseklik. \(\log(n!) \ge \log((n/2)^{n/2}) = (n/2)\log(n/2) = \Theta(n \log n)\). Dolayısıyla her karşılaştırmalı sıralama \(\Omega(n \log n)\)’dir; merge sort bu sınıra ulaştığı için optimaldir.

Cevap: Direct access array sort, her indekste tek öğe sakladığı için benzersiz anahtar ister. Counting sort, her indekste bir zincir (sıra koruyan sequence yapısı) tutar; böylece tekrar eden anahtarları geldikleri sırada saklar. Bu hem tekrarlı anahtarları destekler hem de kararlılık sağlar — ki radix sort’un çalışması için bu şarttır.

Cevap: En önemli basamak, son geçişte sıralanmalı ki nihai sırada baskın olsun. Eğer önce en önemliyi sıralarsak, sonra en az önemliyi sıralamak önceki işi bozar (eşitlikler yeniden dağılır). En az önemliden başlayıp en önemliyle bitirince, kararlı sıralama sayesinde en önemli basamakta eşit olanlar önceki (az önemli) sıralamayı korur. Bu yüzden hem sıra (en az → en önemli) hem de kararlılık zorunludur.

Cevap: Radix sort \(O(n + n \cdot \log_n u)\)’dur. \(u \le n^c\) (sabit \(c\)) ise \(\log_n u = c\) sabittir → \(O(n)\) lineer. \(n^3\)’e kadar anahtarlar için (\(u = n^3\)): her anahtarı base-\(n\) üç basamağa ayırırız (\(\log_n n^3 = 3\), sabit). Üç kararlı counting sort geçişi, her biri \(O(n)\) → toplam \(O(3n) = O(n)\). Anahtarlar \(n\)’in bir polinomuyla sınırlı kaldıkça radix sort lineerdir.

8.12 Egzersizler

Egzersiz 1. Stirling yaklaşımını (\(n! \approx \sqrt{2\pi n}(n/e)^n\)) kullanarak \(\log(n!) = \Theta(n \log n)\)’i göster ve Bölüm 2’deki \((n/2)^{n/2}\) alt sınırıyla karşılaştır.

Egzersiz 2. \(u = n^2\) için, radix sort’un iki basamaklı (base-\(n\)) çalışmasını \(17, 3, 24, 22, 12\) üzerinde elle yürüt; her counting sort geçişinin çıktısını yaz.

Egzersiz 3. Bir sıralamanın kararlı olup olmadığını test eden bir örnek tasarla: aynı anahtarlı ama farklı “yük” taşıyan öğelerle. Selection sort kararlı mıdır?

Egzersiz 4. Python’da counting sort’u tek basamak için yaz ve sorted(..., key=...)’in kararlılığıyla karşılaştır:

def counting_sort(A, key, base):
    chains = [[] for _ in range(base)]
    for x in A:
        chains[key(x)].append(x)      # geldigi sirada ekle -> kararli
    out = []
    for chain in chains:
        out.extend(chain)
    return out

Egzersiz 5. Radix sort’un çalışma süresi \(O(n \cdot \log_n u)\) için, \(u = 2^{32}\) (32-bit tam sayılar) ve \(n = 1000\) olduğunda kaç basamak gerekir? Bu \(n \log n\)’den iyi mi?

8.13 Sonraki Ders İçin Hazırlık

Ders 6 (L6): İkili Ağaçlar — Bölüm 1

Erik Demaine ile, sıralı diziyi (\(O(n)\) ekleme) ve hash tablosunu (worst-case \(O(n)\)) aşan dengeli bir yapıya — ikili ağaçlara — geçiyoruz: tüm işlemler worst-case \(O(\log n)\). (Not: ders akışında araya Problem Oturumu 3 girer.)

UyarıDers 6 Öncesi Yapılacak
  • Bu dersin egzersizlerini, özellikle Egzersiz 4’ü (counting sort) çöz.
  • Üç sıralama yöntemi (direct access → counting → radix) arasındaki ilişkiyi ezberden anlat.
  • Ana cümleyi tekrar oku: “Anahtarlar küçük tam sayıysa, basamak basamak kararlı sıralayarak \(O(n)\)’e inilir.”

8.14 Anahtar Kavramlar (Cheat Sheet)

Kavram Tanım Sayfada
Sıralama alt sınırı Karşılaştırma modelinde \(\Omega(n \log n)\); \(n!\) yaprak Böl. 2
Direct access array sort Benzersiz anahtar, küçük aralık; \(O(n + u)\) Böl. 3
Base-\(n\) ayırma \(k = a \cdot n + b\); \(a = k // n\), \(b = k \bmod n\) Böl. 4
Tuple sort Basamakları en az → en önemli, kararlı sırala Böl. 5
Kararlı (stable) sıralama Eşit anahtarlar girdi sırasını korur Böl. 6
Counting sort Direct access + zincir; kararlı; \(O(n + u)\) Böl. 7
Radix sort \(\log_n u\) basamak + counting sort; \(u \le n^c \to O(n)\) Böl. 8

8.15 Builder ve OMSCS Bağlantıları

İpucu6 köprü

Bu ders, “ne zaman karşılaştırmadan vazgeçilir” sezgisini kurar — köprülerin özeti:

  1. \(n \log n\) alt sınırı → teorik temel: hiçbir karşılaştırmalı sıralama bunu geçemez; modeli değiştirmek (rastgele erişim) tek çıkış.
  2. Counting/radix sort → büyük veri: sabit-uzunluk tam sayı/string anahtarlarda (IP, timestamp, sabit ID) karşılaştırmalı sıralamayı geçer; GPU ve veritabanı sıralaması.
  3. Kararlı sıralama → çok-kolonlu sıralama: SQL ORDER BY a, b, pandas sort_values — hep kararlı, en az önemliden uygulama mantığı.
  4. Base-\(n\) ayırma → genel teknik: büyük problemi sabit sayıda küçük parçaya bölmek (radix, bucket, hash bölmeleri).
  5. Direct access fikri → counting sort ve hashing ortak kökü: “anahtar = indeks” — arama ve sıralamanın aynı sezgisi.
  6. Polinom-sınırlı anahtar → pratik kural: anahtarlar \(n^c\) ile sınırlıysa lineer sırala; değilse merge sort’a dön.

ÖnemliTek bir şey alıp gideceksen

Karşılaştırma modelinde \(n \log n\) bir duvardır — \(n!\) permütasyon onu zorunlu kılar. Ama anahtarların küçük tam sayı olduğunu bilirsen, onları doğrudan indekse çevirip (counting sort) basamak basamak kararlı sıralayarak (radix sort) duvarı aşar, \(O(n)\)’e inersin.