5  Kümeler ve Sıralama

Küme arayüzü, sırasız/sıralı dizi tradeoff’u, permutation/selection/insertion/merge sort ve substitution analizi

NotBölüm bilgisi

5.1 Bu Derste Ne Var?

Ders 2 dizi (sequence) arayüzünü çözdü — öğeleri sıraya göre tutmak. Bu ders ikinci temel arayüze geçer: küme (set) — öğeleri anahtarına göre tutmak. Justin Solomon, set’i çözmenin doğal yolunun sıralama olduğunu gösterir ve dersin yarısını sıralama algoritmalarına ayırır.

Üç temel kavram bu derste yan yana gelir:

  1. Küme arayüzü — anahtarla (key) erişilen bir koleksiyon: find, insert, delete, find_min/max.
  2. Sırasız vs sıralı dizi tradeoff’u — sıralı dizide arama \(O(\log n)\) ama sıralamanın kendisi \(O(n \log n)\) maliyetlidir.
  3. Sıralama algoritmaları — permutation, selection, insertion ve merge sort; özyineleme + substitution ile çalışma süresi analizi.

“there’s an object called an interface, which is just a program specification.” — Justin, 1:42

flowchart TD
    A["Ders 4: Kümeler ve Sıralama"] --> B["Küme (set) arayüzü<br/>anahtarla erişim"]
    A --> S["Sıralama problemi<br/>girdi A → sıralı B"]
    B --> U["Sırasız Dizi<br/>find O(n)<br/>lineer tarama"]
    B --> O["Sıralı Dizi<br/>find O(log n) ikili arama<br/>find_min/max O(1)"]
    O -->|kurma maliyeti| S
    S --> P["Permutation sort<br/>Ω(n!·n) · kaba kuvvet"]
    S --> SE["Selection sort<br/>Θ(n²) · en büyüğü sona"]
    S --> IN["Insertion sort<br/>Θ(n²) · yerine kaydır"]
    S --> ME["Merge sort<br/>Θ(n log n) · böl-yönet"]
    ME --> R["T(n)=2T(n/2)+Θ(n)<br/>two-finger merge"]

    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 B,S,O branch
    class U,P,SE,IN,ME,R leaf
Şekil 5.1: Ders 4’ün kavram haritası: küme arayüzünden iki dizi gerçekleştirimine (sırasız dizi → find O(n) / sıralı dizi → find O(log n)), oradan sıralama problemine ve dört sıralama algoritmasına (permutation Ω(n!·n), selection Θ(n²), insertion Θ(n²), merge Θ(n log n)).
İpucuBuilder Notu — Set Arayüzü ve Python dict/Veritabanı

Bu dersin küme (set) arayüzü, her veri katmanının altında yatan “anahtarla eriş” desenidir — Python’dan veritabanlarına:

  • Geriye → Python (Phase 1): Python set/dict tam da bu set arayüzünün gerçekleştirimidir; Solomon “bu çirkin detayları sizin için hallederler” der. key in d ortalama \(O(1)\) görünür, ama o sabit zamanın nasıl elde edildiği (Ders 4 / Hashing) burada başlayan sorudur.
  • Geriye → Calculus (Phase 1): \(n^2\) ile \(n \log n\) büyüme farkı — bir milyar öğede biri saatler, diğeri saniyeler. Asimptotik mertebeleri karşılaştırma disiplini doğrudan fonksiyon büyümesi sezgisidir.
  • İleriye → veritabanı/arama: sıralı/indeksli veri, ikili aramayla \(O(\log n)\) sorgu; her veritabanı indeksinin (B-tree) temeli.
  • İleriye → OMSCS / böl-yönet: merge sort, “böl ve yönet + substitution” kalıbının ilk örneği; CS 6515’te (Graduate Algorithms) her yerde.

Tek cümle: Bir kümeyi sıralı tutmak, aramayı \(O(\log n)\)’e indirir — ama bu hızın bedeli, baştaki \(O(n \log n)\)’lik sıralamadır.

5.2 Arayüz Tekrarı: Küme Nedir?

Ders 1’de tanıtılan ayrım burada da merkezdedir: arayüz bir program spesifikasyonudur (hangi işlemler destekleniyor); veri yapısı o arayüzü perde arkasında gerçekleştiren somut yapıdır.

“there’s an object called an interface, which is just a program specification… a data structure, which is a way to actually implement an interface.” — Justin, 1:42

Bir küme (set) “bir yığın şey”dir: içine öğe eklersin ve “bu öğe var mı?” diye sorarsın. Her öğe bir anahtarla (key) ilişkilidir — örneğin sınıftaki öğrencilerin ID numarası. Aramayı anahtarla yaparsın; bulunca öğenin geri kalan bilgisine (isim, vb.) erişirsin.

5.3 Küme (Set) Arayüzü

Set arayüzü şu işlemleri destekler:

  • build(A): bir iterable A’dan küme kur.
  • len(): içindeki öğe sayısı.
  • find(k): anahtarı k olan öğeyi döndür (yoksa null).
  • insert(x) / delete(k): dinamik işlemler — kümeyi değiştirir.
  • find_min() / find_max(): en küçük / en büyük anahtarlı öğe.
  • find_prev(k) / find_next(k): sıralı komşular.

Burada x tüm nesneyi (ID + isim + …), k ise aramada kullanılan anahtarı temsil eder. Bu bir arayüzdür — nasıl gerçekleştirildiği henüz söylenmedi.

5.4 Küme mi, Dizi mi?

İki temel arayüzün farkı, öğeleri neye göre tuttuğudur:

  • Dizi (sequence): öğeler dışsal (extrinsic) bir sıraya göre — ilk, onuncu, son. Pozisyonla erişim.
  • Küme (set): öğeler içsel (intrinsic) anahtarlarına göre. Değerle (key) erişim.

Aynı veri her iki arayüzle de tutulabilir, ama sorular farklıdır: dizi “5. öğe ne?” diye sorar, küme “anahtarı 42 olan öğe var mı?” diye sorar.

5.5 Sırasız Dizi ile Küme

En basit gerçekleştirim: öğeleri sırasız bir dizide (unordered array), gelişigüzel sıralamayla sakla. Kurması kolaydır — büyük bir dizi al, her şeyi içine at.

“an unordered array is a perfectly reasonable way to implement this set interface. And then searching that array it will take linear time” — Justin, 12:00

Ama her arama, diziyi baştan sona taramayı gerektirir: find(k) en kötü durumda \(O(n)\). Aslında tüm işlemler (build, insert, delete, find_min) lineer zamandır. Basit ama yavaş. Şekil 5.2 bu sırasız gerçekleştirimi, hemen ardından gelen sıralı alternatifle yan yana gösterir.

5.6 Sıralı Dizi ile Küme

Daha iyisi: öğeleri anahtara göre sıralı bir dizide tut. Artık:

  • find_min() / find_max(): ilk / son öğe → \(O(1)\).
  • find(k): ikili arama (binary search) ile diziyi her adımda yarıya böl → \(O(\log n)\).

“if my array is sorted, how long does it take for me to search for any given element? … Log n time.” — Justin, 16:24

Bir milyar öğede sırasız dizi ~bir milyar işlem, sıralı dizi yalnızca ~30 işlem (\(\log_2 10^9 \approx 30\)) ister. Ama bir bedel var: sıralı diziyi elde etmek = sıralama, ve sıralama \(O(n \log n)\) zaman alır. Dersin geri kalanı bu sıralamayı çözer. Şekil 5.2 iki gerçekleştirimi karşılaştırır; Şekil 5.3 ise ikili aramanın yarıya-bölme mekaniğini tek tek izler.

Şekil 5.2: Küme arayüzünü diziyle gerçekleştirmenin iki yolu ve arama maliyetinin ayrışması. Üstte sırasız dizi: find(7) öğeyi bulana dek baştan sona tarar (soluk slate okları), öğe nerede olursa olsun en kötü durumda her hücreye bakılır → \(O(n)\); find_min/max de aynı şekilde tüm diziyi tarar. Altta aynı veri sıralı: find(7) ikili aramayla yalnızca ortaları inceler (amber, 1-2-3 sırasıyla: index 4 → 6 → 5), her adım aralığı yarıya böler → \(O(\log n)\); üstelik find_min = ilk öğe ve find_max = son öğe uçlardan doğrudan okunur → \(O(1)\). Bedel: sıralı diziyi kurmak bir kez \(O(n \log n)\) sıralama gerektirir — ama sonraki her arama logaritmik kalır (Şekil 5.3 bunu ayrıntılandırır).
İpucuBuilder Notu — O(log n) ve Veritabanı İndeksleri (B-tree)

“Sıralı dizi + ikili arama = \(O(\log n)\) arama” sezgisi, her ölçeklenebilir veri sisteminin kalbindedir:

  • İleriye → veritabanı indeksleri: Bir tabloya indeks eklemek, anahtarları sıralı bir yapıda (B-tree, sıralı dizinin disk-dostu çok-yollu genellemesi) tutmak demektir; WHERE id = 42 sorgusu tam tamına ikili aramadır → milyarlarca satırda ~30 disk erişimi. İndekssiz sorgu ise sırasız diziye eşdeğer: tam tablo taraması, \(O(n)\).
  • İleriye → arama motoru posting list’leri: Ters indekslerdeki belge ID listeleri sıralı tutulur; iki listenin kesişimi (AND sorgusu) tam da two-finger merge mantığıyla, sıralılıktan faydalanarak yapılır.
  • Bedel-fayda dengesi: İndeks “ücretsiz” değildir — onu kurmak ve güncel tutmak (her insert’te sıralı yapıyı koruma) maliyettir, tıpkı sıralı diziyi kurmanın \(O(n \log n)\) bedeli gibi. “Çok okunan, az yazılan” veride indeks kazandırır.

5.7 Sıralama Problemi ve Sözlük

Sıralama problemi: girdi, \(n\) anahtardan oluşan bir dizi A; çıktı, aynı öğelerin sıralı bir dizisi B. İki terim algoritmaları ayırır:

  • Yıkıcı (destructive): yeni bir B dizisi ayırmak yerine, A’nın üzerine sıralı hâlini yazar (C++ ve Python sort’u böyledir).
  • Yerinde (in-place): yıkıcı ve ek bellek kullanmaz (sabit \(O(1)\) fazlası dışında — döngü sayaçları gibi).

“a destructive algorithm is one that just overwrites A with a sorted version of A… in place, meaning that they also don’t use extra memory” — Justin, 20:13

5.8 Permütasyon Sıralaması

En basit (ve en kötü) sıralama: bir listenin sıralı hâli, onun bir permütasyonudur. O hâlde tüm permütasyonları listele, her birinin sıralı olup olmadığını kontrol et.

“list every possible permutation, and then just double check which one’s in the right order.” — Justin, 22:04

\(n\) öğenin \(n!\) permütasyonu vardır; her birinin sıralılığını kontrol etmek \(\Theta(n)\)’dir. Toplam \(\Omega(n! \cdot n)\)\(n!\)’den bile kötü. (Burada \(\Omega\) kullanılır çünkü permütasyonları üretmenin maliyeti en az bu kadardır — bir alt sınır.) Pratikte tamamen kullanılamaz; ama doğruluğu ispatlaması çok kolaydır.

5.9 Seçmeli Sıralama (Selection Sort)

Daha akıllı bir algoritma. 6.006’da onu — alışılmadık biçimde — özyinelemeli yazarız (doğruluk ve süre analizini kolaylaştırmak için; pratikte iki döngüyle yazılır).

Önce bir yardımcı: prefix_max(A, i)A’nın 0’dan i’ye kadarki en büyük öğesinin indeksini bulur. Solomon’un “derin gözlemi”: 0..i aralığındaki en büyük öğe ya tam i’dedir ya da i’den önceki bir indekstedir.

“the biggest element from 0 to i… Either it’s at index i… or it has index less than i. … Another term for this is induction.” — Justin, 31:15

def prefix_max(A, i):
    if i == 0:
        return 0
    j = prefix_max(A, i - 1)      # 0..i-1'in en buyugu (tumevarim hipotezi)
    if A[i] > A[j]:
        return i
    return j

Seçmeli sıralama: i = n−1’den başla; 0..i’nin en büyüğünü prefix_max ile bul, indeks i’ye swap et, sonra 0..i−1’i özyinelemeli sırala (somut bir iz için Şekil 5.4).

def selection_sort(A, i):
    if i > 0:
        j = prefix_max(A, i)
        A[i], A[j] = A[j], A[i]   # en buyugu sona koy
        selection_sort(A, i - 1)  # solu sirala

Çalışılan Örnek — substitution ile çalışma süresi. prefix_max için: \(S(n) = S(n-1) + \Theta(1)\). \(S(n) = cn\) tahmin et, yerine koy: \(cn = c(n-1) + \Theta(1) \to \Theta(1) = c\), sabit. Yani \(S(n) = \Theta(n)\). Seçmeli sıralama için: \(T(n) = T(n-1) + \Theta(n)\) (her çağrı bir prefix_max = \(\Theta(n)\)). \(T(n) = cn^2\) tahmin et: \(cn^2 = c(n-1)^2 + \Theta(n) = cn^2 - 2cn + c + \Theta(n) \to \Theta(n) = 2cn - c\), tutarlı. Yani \(T(n) = \Theta(n^2)\). Şekil 5.6 bu \(n\) seviyeli yineleme ağacını, merge sort’un \(\log n\) seviyesiyle yan yana koyar.

Şekil 5.4: Seçmeli sıralama izi (Solomon \(\S 8\)): dizi sağdan sola sıralanır. Her dış adımda prefix_max(0..i) aralığın en büyük öğesini bulur (amber vurgulu) ve onu pozisyon \(i\)’ye swap eder (amber yay) — böylece sağ uçta giderek büyüyen bir sıralı kuyruk (soluk hücreler) oluşur. Burada \([3,5,1,6,4,2]\) dört geçişte tamamen sıralanır: \(6 \to 5 \to 4 \to 3\) sırayla sona yerleşir, en altta sonuç \([1,2,3,4,5,6]\). \(n\) adımın her biri \(\Theta(n)\)’lik bir prefix_max taraması içerir → \(T(n)=T(n-1)+\Theta(n)=\Theta(n^2)\).
İpucuBuilder Notu — Yineleme Analizi Disiplini

prefix_max ve selection_sort’taki “form tahmin et → yerine koy → doğrula” (substitution) hareketi, her özyinelemeli maliyeti kapalı bir formüle bağlamanın temel disiplinidir:

  • İleriye → ML kernel maliyetleri: Bir transformer’da self-attention’ın \(O(n^2)\) maliyeti (her token diğer her token’a bakar) tam bu tür bir sayımla türetilir — ve neden uzun bağlamın pahalı olduğunun, FlashAttention/lineer-attention gibi optimizasyonların neden gerektiğinin cevabıdır.
  • İleriye → OMSCS CS 6515: Graduate algoritma derslerinde her dinamik programlama / böl-yönet çözümünün ardından bir yineleme bağıntısı yazılır ve substitution (veya master teoremi) ile çözülür; bu dersin alıştırması oraya doğrudan taşınır.
  • Sezgi: Selection sort’un \(T(n)=T(n-1)+\Theta(n)\)’i, “her adım problemi yalnız bir azaltır” demektir; bu yüzden \(n + (n-1) + \dots + 1 = \Theta(n^2)\). Yineleme bağıntısı, performansın nedenini koddan önce gösterir.

5.10 Eklemeli Sıralama (Insertion Sort)

Seçmeli sıralamanın “tersi”: önce 0..i−1’i sırala, sonra i’inci öğeyi sıralı kısma doğru yerine kaydır. O da özyinelemeli yazılır ve yine \(\Theta(n^2)\)’dir (her ekleme en kötü durumda lineer kaydırma). Solomon zaman darlığından detayını atlar; argüman seçmeli sıralamayla aynıdır.

5.11 Birleştirmeli Sıralama (Merge Sort)

İlk gerçekten verimli sıralama: böl ve yönet. Diziyi ortadan ikiye böl, her yarıyı özyinelemeli sırala, sonra iki sıralı yarıyı birleştir (merge).

Birleştirmenin püf noktası iki parmak (two-finger) tekniğidir: her sıralı listenin sonuna birer parmak koy, büyük olanı sonuca al, o parmağı sola kaydır. İki yarı zaten sıralı olduğundan, parmağın soluna hiç bakmana gerek kalmaz → birleştirme \(\Theta(n)\) (lineer).

“I’m going to take two sorted lists. And I’m going to make a new sorted list, which is twice as long, by using two fingers… it takes linear time to merge.” — Justin, 44:40

def merge_sort(A, a, b):          # A[a:b] araligini sirala
    if b - a > 1:
        c = (a + b) // 2          # orta (center)
        merge_sort(A, a, c)       # solu sirala
        merge_sort(A, c, b)       # sagi sirala
        merge(A, a, c, b)         # iki sirali yariyi birlestir (two-finger)

Çalışılan Örnek — substitution ile çalışma süresi. Merge sort iki kez yarı boyutta çağrılır + \(\Theta(n)\) birleştirme: \(T(n) = 2 \cdot T(n/2) + \Theta(n)\). \(T(n) = cn \log n\) tahmin et, yerine koy: \(cn \log n = 2 \cdot c(n/2) \cdot \log(n/2) + \Theta(n) = cn(\log n - \log 2) + \Theta(n) = cn \log n - cn \log 2 + \Theta(n)\). İki taraftan \(cn \log n\) gider; \(\Theta(n) = cn \log 2\), tutarlı (\(\log 2\) sabit). Yani \(T(n) = \Theta(n \log n)\) — sıralı diziyi vaat edilen sürede elde ettik. Şekil 5.5 bu böl-yönet akışını ve tek bir two-finger merge adımını birlikte gösterir.

Şekil 5.5: Birleştirmeli sıralama = böl-yönet. Üstte özyineleme ağacı: dizi her seviyede tam ortadan ikiye bölünür (aşağı, böl), \(\lceil \log_2 n \rceil + 1\) seviye sonra yapraklara (tek öğe, zaten sıralı; slate) iner. Sonra aşağıdan yukarı her düğüm iki sıralı çocuğunu two-finger merge ile birleştirir (amber, yukarı ok). Altta tek bir two-finger adımı: iki sıralı liste \([1,2,7,8]\) ve \([3,4,6,9]\) üstündeki iki parmak (\(i\), \(j\)) karşılaştırılır, küçük olan (\(\min(1,3)=1\)) sonuca alınır ve o parmak kaydırılır — her öğeye tam bir kez dokunulur, bu yüzden birleştirme bir seviyede \(\Theta(n)\). \(\log n\) seviye \(\times\) seviye-başına \(\Theta(n)\) iş: \(T(n) = 2T(n/2) + \Theta(n) = \Theta(n \log n)\).
Şekil 5.6: Neden seçmeli sıralama \(\Theta(n^2)\) ama birleştirmeli sıralama \(\Theta(n\log n)\) — fark yineleme ağacının seviye sayısındadır (\(n=8\)). Sol (selection): \(T(n)=T(n-1)+\Theta(n)\) — her adım problemi yalnız BİR azaltır, alt-problem boyutu \(8, 7, 6, \dots, 1\) olur; bu yüzden \(8\) seviye (genel: \(n\) seviye). Çubuk genişlikleri o seviyedeki alt-problem boyutudur (\(T(8)\) en uzun, \(T(1)\) en kısa). \(n\) seviye \(\times\) ortalama \(\Theta(n)\)\(\Rightarrow \Theta(n^2)\). Sağ (merge): \(T(n)=2T(n/2)+\Theta(n)\) — her seviye problemi YARIYA böler, alt-problem sayısı \(1, 2, 4, 8\) ve boyutları \(8, 4, 2, 1\) olur; bu yüzden yalnız \(\lceil\log_2 8\rceil+1 = 4\) seviye (genel: \(\log n\) seviye). Her seviyede toplam genişlik (toplam iş) sabit \(\Theta(n)\) kalır — alt-problemler küçülürken sayıları artar. \(\log n\) seviye \(\times \Theta(n)\)/seviye \(\Rightarrow \Theta(n\log n)\). İki algoritma da seviye başına \(\Theta(n)\) iş yapar; tek fark seviye sayısı: \(n\)’e karşı \(\log n\)\(n=10^6\)’da bu, bir milyon kata karşı \(\approx 20\) kat demektir.
İpucuBuilder Notu — Böl-Yönet ve OMSCS / quicksort-FFT

Merge sort’un \(T(n) = 2T(n/2) + \Theta(n) = \Theta(n \log n)\) yinelemesi, tüm verimli özyinelemeli algoritmaların habercisidir:

  • İleriye → OMSCS CS 6515 çekirdek paradigması: Graduate algoritmada böl-ve-yönet ayrı bir ünitedir; quicksort, ikili arama, en yakın nokta çifti ve hepsi aynı “böl → özyinele → birleştir” kalıbını ve master teoremini paylaşır.
  • İleriye → FFT (\(\Theta(n \log n)\)): Hızlı Fourier Dönüşümü, polinomu çift/tek katsayılara böler, her yarıyı özyinelemeli dönüştürür, sonra birleştirir — merge sort’un birebir aynı yineleme ağacı. ML’de hızlı evrişim (convolution) ve sinyal işleme buna dayanır.
  • Pratik nüans: Merge sort kararlıdır ama \(\Theta(n)\) ek alan ister; quicksort yerinde ve genelde daha hızlıdır ama en kötü durumda \(\Theta(n^2)\). “Hangi \(\Theta(n \log n)\) sıralama” kararı bu uzay/kararlılık/en-kötü-durum dengeleriyle verilir.

5.12 Sıralama Algoritmaları — Karşılaştırma

Algoritma Çalışma süresi Yerinde mi? Yöntem
Permutation sort \(\Omega(n! \cdot n)\) Hayır Kaba kuvvet (tüm permütasyonlar)
Selection sort \(\Theta(n^2)\) Evet En büyüğü seç, sona koy, özyinele
Insertion sort \(\Theta(n^2)\) Evet Solu sırala, yeni öğeyi yerine kaydır
Merge sort \(\Theta(n \log n)\) Hayır (\(\Theta(n)\) ek alan) Böl ve yönet, two-finger merge

Şekil 5.7 aynı dört satırı, çalışma süresini büyümeye göre renklendirerek görselleştirir.

Şekil 5.7: Dört sıralama algoritmasının karşılaştırması (§11): her satır bir algoritma, sütunlar çalışma süresi, yerinde mi? ve yöntem. Çalışma süresi büyümeye göre renklenir — permütasyon sıralama \(\Omega(n! \cdot n)\) (tüm permütasyonları deneyen kaba kuvvet, en kötü; koyu amber); seçmeli ve eklemeli sıralama \(\Theta(n^2)\) (amber); birleştirmeli sıralama \(\Theta(n\log n)\) (yeşilimsi-slate = en iyi). “Yerinde mi?” sütunu ek alan kullanımını gösterir: seçmeli/eklemeli Evet (kopya üstünde \(O(1)\) ek alanla swap), permütasyon ve birleştirmeli Hayır (merge \(\Theta(n)\) ek alan ister). \(n = 10^6\) ölçeğinde \(n^2\) ile \(n\log n\) arasındaki fark yaklaşık bir milyon kattır — bu yüzden büyük veride birleştirmeli sıralama seçilir.
İpucuBuilder Notu — n² vs n log n Ölçek Sezgisi

Tablodaki \(\Theta(n^2)\) ile \(\Theta(n \log n)\) arasındaki fark, soyut değil — pratik bir uçurumdur:

  • Ölçek hesabı: \(n = 10^6\)’da \(n^2 = 10^{12}\), ama \(n \log n \approx 10^6 \times 20 = 2 \times 10^7\). Oran \(\approx 50.000\) kat; saniyeler ile günler arasındaki fark. \(n = 10^9\)’da uçurum bir milyon kata yaklaşır.
  • İleriye → ML veri ön-işleme: Milyonlarca örneği sıralamak (deduplication, tokenizasyon istatistikleri, nearest-neighbor için ön-sıralama) gerçek bir maliyettir; “hangi sıralama / hangi veri yapısı” kararı eğitim pipeline’ının darboğazını belirler.
  • Disiplin: Bir builder, \(n\)’in büyüklük mertebesini görür görmez hangi karmaşıklığın “geçeceğini” tahmin eder: \(n \le 10^4\) ise \(n^2\) tolere edilebilir; \(n \ge 10^6\) ise \(n \log n\) (veya daha iyisi) zorunludur. Bu tablo o sezginin temelidir.

5.13 Bu Dersin Özeti

  1. Küme (set) arayüzü öğeleri anahtara göre tutar: find, insert, delete, find_min/max.
  2. Sırasız dizi ile küme: tüm işlemler \(O(n)\) (find = lineer tarama).
  3. Sıralı dizi ile küme: find_min/max \(O(1)\), find \(O(\log n)\) (ikili arama) — ama kurma maliyeti \(O(n \log n)\).
  4. Sıralama sözlüğü: yıkıcı (destructive), yerinde (in-place).
  5. Permutation sort \(\Omega(n! \cdot n)\) — kullanılamaz kötü örnek.
  6. Selection/Insertion sort \(\Theta(n^2)\); özyineleme + substitution ile analiz.
  7. Merge sort \(\Theta(n \log n)\): böl-yönet + lineer two-finger birleştirme; \(T(n) = 2T(n/2) + \Theta(n)\).
ÖnemliTek Bir Cümle

Sıralama, bir kümeyi “anahtarla aranabilir” kılmanın bedelidir; merge sort bu bedeli böl-yönet ile \(O(n \log n)\)’e indirir.

5.14 Kontrol Soruları

Cevap: İkisinde de insert/delete temelde \(O(n)\)’dir. Ayrışma aramada olur: sırasız dizide find(k) ve find_min/max her zaman \(O(n)\) (lineer tarama); sıralı dizide find(k) ikili aramayla \(O(\log n)\), find_min/max ise \(O(1)\) (ilk/son öğe). Karşılığında sıralı diziyi kurmak \(O(n \log n)\) sıralama gerektirir — tek seferlik bir ön maliyet.

Cevap: Solomon permütasyonları üreten algoritmayı tanımlamaz, “sihirli” bir fonksiyon varsayar. Ama emin olduğu şey: \(n\) öğenin \(n!\) permütasyonu vardır ve her birinin sıralılığını kontrol etmek \(\Theta(n)\)’dir, dolayısıyla iş en az \(n! \cdot n\) kadardır. “En az” bir alt sınırdır\(\Omega\). Kesin çalışma süresi verilmediği için \(O\) (üst sınır) iddia edilmez.

Cevap: \(T(n) = cn^2\) tahmin et ve yerine koy: \(cn^2 \stackrel{?}{=} c(n-1)^2 + \Theta(n)\). Sağ taraf \(= c(n^2 - 2n + 1) + \Theta(n) = cn^2 - 2cn + c + \Theta(n)\). İki taraftan \(cn^2\) çıkar: \(0 \stackrel{?}{=} -2cn + c + \Theta(n)\). Bu, \(\Theta(n) = 2cn - c\) demektir; sol ve sağ aynı mertebede (lineer), dolayısıyla tutarlı. Tahmin doğrulandı: \(T(n) = \Theta(n^2)\). Sezgi: \(n + (n-1) + \dots + 1 = n(n+1)/2 = \Theta(n^2)\).

Cevap: Selection sort: \(T(n) = T(n-1) + \Theta(n)\) → her adımda problemi bir azaltır, \(n\) seviye \(\times \Theta(n) = \Theta(n^2)\). Merge sort: \(T(n) = 2T(n/2) + \Theta(n)\) → her adımda problemi yarıya böler, yalnızca \(\log n\) seviye \(\times\) her seviyede toplam \(\Theta(n)\)\(= \Theta(n \log n)\). Farkın kaynağı: lineer azaltma (\(n\) seviye) yerine logaritmik bölme (\(\log n\) seviye).

5.15 Egzersizler

Egzersiz 1. Sıralı bir dizide find_prev(k) ve find_next(k) işlemlerini tasarla. İkili aramayla \(O(\log n)\)’de nasıl yapılır?

Egzersiz 2. prefix_max için tümevarımla doğruluğu tam yaz: taban durum (\(i = 0\)) ve tümevarım adımı (\(i\)’deki öğe ile 0..i−1’in en büyüğünün maksimumu).

Egzersiz 3. Insertion sort’u özyinelemeli yaz ve \(T(n) = T(n-1) + O(n)\) yinelemesini substitution ile \(\Theta(n^2)\)’ye bağla. En iyi durum (zaten sıralı girdi) ne olur?

Egzersiz 4. Python’da merge sort ile yerleşik sorted’ı farklı \(n\)’lerde time.perf_counter ile karşılaştır; \(n \log n\) eğrisini gözlemle:

import time, random

def measure(n):
    A = [random.random() for _ in range(n)]
    t0 = time.perf_counter()
    sorted(A)
    print(n, time.perf_counter() - t0)

for n in (10_000, 100_000, 1_000_000):
    measure(n)

Egzersiz 5. İki sıralı listeyi birleştiren two-finger merge fonksiyonunu yaz ve neden \(\Theta(n)\) olduğunu (her öğeye bir kez dokunulduğunu) açıkla.

5.16 Sonraki Ders İçin Hazırlık

Ders 4 (L4): Hashing

Jason Ku ile, sıralı dizinin \(O(\log n)\) aramasını bile geçen bir yapıya — hash tablosuna — geçiyoruz: find/insert/delete beklenen \(O(1)\). Sıralamadan vazgeçip anahtarları doğrudan adreslere “serpiştirmenin” maliyeti ve riski (çakışma) ele alınır. (Not: ders akışında araya Problem Oturumu 2 girer.)

UyarıDers 4 Öncesi Yapılacak
  • Bu dersin egzersizlerini, özellikle Egzersiz 4’ü (merge sort ölçümü) çöz.
  • Sıralama tablosunu (Bölüm 11) ezberden çizebil.
  • Ana cümleyi tekrar oku: “Sıralama, bir kümeyi anahtarla aranabilir kılmanın bedelidir.”

5.17 Anahtar Kavramlar (Cheat Sheet)

Kavram Tanım Sayfada
Küme (set) arayüzü Anahtarla erişilen koleksiyon (find, insert, find_min) Böl. 2
Sırasız dizi Küme gerçekleştirimi; tüm işlemler \(O(n)\) Böl. 4
Sıralı dizi + ikili arama find \(O(\log n)\), find_min/max \(O(1)\); kurma \(O(n \log n)\) Böl. 5
Yıkıcı / yerinde Destructive: A’yı ezer; in-place: ek \(O(1)\) alan Böl. 6
Permutation sort Tüm permütasyonlar; \(\Omega(n! \cdot n)\) Böl. 7
Selection sort En büyüğü seç-sona-koy; \(\Theta(n^2)\) Böl. 8
Merge sort Böl-yönet + two-finger merge; \(\Theta(n \log n)\) Böl. 10
Substitution yöntemi Yineleme için form tahmin et, yerine koy, doğrula Böl. 8, 10
Two-finger merge İki sıralı listeyi \(\Theta(n)\)’de birleştirme Böl. 10

5.18 Builder ve OMSCS Bağlantıları

İpucu6 köprü

Bu ders, ileri algoritma ve veri mühendisliğinin temel dilini kurar — köprülerin özeti:

  1. Set arayüzü → Python dict/set, veritabanı tabloları: anahtarla erişim her veri katmanının temeli.
  2. Sıralı dizi + ikili arama → veritabanı indeksleri (B-tree), arama motoru sıralı gönderim listeleri: \(O(\log n)\) sorgu.
  3. Böl ve yönet (merge sort) → OMSCS CS 6515’in çekirdek paradigması; quicksort, FFT, en yakın çift hep aynı kalıp.
  4. Substitution / yineleme analizi → her özyinelemeli algoritmanın çalışma süresini kapalı forma bağlama disiplini.
  5. Destructive / in-place → bellek-kısıtlı sistemler: yerinde sıralama, ek tampon ayıramayan gömülü ortamlarda kritik.
  6. \(n^2\) vs \(n \log n\) → ölçek sezgisi: \(n = 10^6\)’da \(n^2\) bir milyon kat daha yavaş; “hangi sıralama” kararının pratik bedeli.

ÖnemliTek bir şey alıp gideceksen

Bir kümeyi sıralı tutmak aramayı \(O(\log n)\)’e indirir, ama sıralamanın kendisi \(O(n \log n)\)’dir. Merge sort bu bedeli “böl ve yönet” ile öder; ve \(T(n) = 2T(n/2) + \Theta(n)\) yinelemesi, tüm verimli özyinelemeli algoritmaların habercisidir.