20  Dijkstra

Ağırlıklar negatif değilse mesafe en kısa yol boyunca artar; bu yüzden düğümleri bir öncelik kuyruğundan artan mesafe sırasında çekip kenarlarını gevşeterek tek-kaynak en kısa yolu çözeriz — değiştirilebilir öncelik kuyruğu decrease_key ile gevşeyen tahminleri günceller, BFS’in ağırlıklı genellemesi, neredeyse doğrusal O(V log V + E)

NotBölüm bilgisi
  • Ku’nun videosu: YouTube — Lecture 13: Dijkstra (~57 dk)
  • OCW sayfası: MIT 6.006 Lecture 13: Dijkstra
  • Seri: MIT 6.006 — Introduction to Algorithms (Spring 2020) — Ders 19 (L13)
  • Hoca: Jason Ku (ağırlıklı en kısa yollar ünitesinin negatif-olmayan ağırlık için hızlı algoritması)
  • Okuma süresi: ~26 dk

Bir önceki ders (Ders 18) Bellman-Ford herhangi bir çizgede çalışıyordu ama \(O(V \cdot E)\) — yoğun çizgede \(O(V^3)\) — yavaştı. Bu ders, ağırlıklar negatif değilse çok daha hızlı bir algoritma verir: kaynaktan dışa artan mesafe sırasında ilerleyen Dijkstra, neredeyse doğrusal \(O(V \log V + E)\).

20.1 Bu Derste Ne Var?

Bellman-Ford (Ders 18) herhangi bir çizgede çalışıyordu ama \(O(V \cdot E)\) (yoğun çizgede \(O(V^3)\)). Bu ders (Jason Ku), ağırlıklar negatif değilse çok daha hızlı bir algoritma verir: Dijkstra. Negatif çevrim derdi olmadığından, kaynaktan dışa artan mesafe sırasında ilerler ve \(O(V \log V + E)\) — doğrusala yakın — süreye iner.

“If weights greater than or equal to 0, then distances increase along shortest paths.” — Ku, 8:35

Üç ana fikir:

  1. Negatif olmayan ağırlık → tekdüze mesafe — en kısa yol boyunca mesafe azalmaz; bu, “kaynaktan dışa büyüyen küre” sezgisini mümkün kılar.
  2. Artan sıra bilinirse DAG relaxation — düğümleri artan mesafe sırasında işleyebilseydik, çizgeyi bir DAG’a çevirip doğrusal çözerdik.
  3. Değiştirilebilir öncelik kuyruğu — sıradaki en yakın düğümü verimlice bulmak için decrease_key destekli bir öncelik kuyruğu.
flowchart TD
    A["Ders 19 (L13): Dijkstra"] --> G1["Gözlem 1: mesafe ARTAR"]
    G1 --> G1a["ağırlık ≥ 0 → en kısa yol boyunca<br/>mesafe azalmaz; dışa büyüyen küre"]
    A --> G2["Gözlem 2: sıra bilinirse DAG"]
    G2 --> G2a["artan mesafe sırası → DAG relaxation<br/>doğrusal; sıfır kenarı birleştir"]
    A --> P["değiştirilebilir öncelik kuyruğu"]
    P --> Pa["build · delete_min · decrease_key<br/>id ile anahtar düşür · Ders 12 + sözlük"]
    A --> D["algoritma + doğruluk"]
    D --> Da["en yakını çıkar → gevşet → decrease_key<br/>gevşetme güvenli + çıkınca δ"]
    A --> T["öncelik kuyruğu seçimi → süre"]
    T --> Ta["dizi V² · ikili yığın E log V<br/>Fibonacci yığını E + V log V"]

    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 G1,G2,P,D,T branch
    class G1a,G2a,Pa,Da,Ta leaf
Şekil 20.1: Ders 19’un (L13) kavram haritası: kök = Dijkstra (Ku) — negatif olmayan ağırlıklı tek-kaynak en kısa yol, BFS’in ağırlıklı genellemesi. Beş dal — (1) Gözlem 1: ağırlık negatif değilse mesafe en kısa yol boyunca artar; kaynaktan dışa büyüyen küre sezgisi. (2) Gözlem 2: düğümlerin artan mesafe sırası bilinirse çizge bir DAG’a iner → DAG relaxation doğrusal çözer; sıfır ağırlıklı kenarlar birleştirilir. (3) değiştirilebilir öncelik kuyruğu: build, delete_min, decrease_key; id ile anahtarı düşür; öncelik kuyruğu (Ders 12) artı çapraz bağ sözlüğü. (4) algoritma artı doğruluk: en yakını çıkar, kenarları gevşet, decrease_key ile güncelle; gevşetme güvenli artı çıkınca delta. (5) öncelik kuyruğu seçimi süreleri belirler: dizi kare V, ikili yığın E log V, Fibonacci yığını E artı V log V. Sonuç: BFS’i ağırlıklı dünyaya taşır; sırrı tek gözlem, negatif yoksa mesafe artar.
İpucuBuilder Notu — Negatif Yoksa Açgözlülük Doğru

Bellman-Ford genel çizgeyi \(O(V \cdot E)\)’de çözüyordu. Dijkstra, ağırlıkların negatif olmadığı kabulüyle tek bir gözlemden güç alır: mesafe en kısa yol boyunca artar, dolayısıyla “en yakın düğümü kesinleştir, bir daha dokunma” açgözlü stratejisi doğrudur. Bedeli \(V \cdot E\) değil, neredeyse doğrusal \(O(V \log V + E)\)’dir.

  • İleriye → GPS/yönlendirme: Dijkstra, Google Maps/Waze ve ağ yönlendirmenin (OSPF link-state) çekirdek algoritmasıdır.
  • İleriye → sezgisel arama: Dijkstra’ya sezgisel (heuristic) eklenince A* olur; oyun yol bulma ve robotik hareket planlamada standart.
  • İleriye → öncelik kuyruğu seçimi: array (yoğun) vs binary heap (seyrek) vs Fibonacci heap (teorik) — veri yapısı seçimi karmaşıklığı belirler.
  • Geriye → DAG relaxation (Ders 16) + öncelik kuyruğu (Ders 12): Dijkstra ikisini birleştirir.

Tek cümle: Negatif ağırlık yoksa mesafe en kısa yol boyunca artar; bu sayede düğümleri artan mesafe sırasında bir öncelik kuyruğundan çekip kenarlarını gevşeterek SSSP’yi \(O(V \log V + E)\)’de çözeriz — BFS’in ağırlıklı genellemesi.

20.2 1. Manzara: Üç Algoritma ve Dijkstra’nın Yeri

Şimdiye dek ağırlıklı SSSP için üç yol gördük: (1) BFS (ağırlıkları tek-kenarlara açarak — yalnız pozitif ve küçük-toplamlı), (2) DAG relaxation (çevrimsiz, herhangi ağırlık, doğrusal), (3) Bellman-Ford (genel, negatif çevrim dahil, \(O(V \cdot E)\)). Bellman-Ford yoğun çizgede \(O(V^3)\) — yavaş.

Çoğu gerçek problemde ağırlıklar negatif değildir (“evime negatif mesafe yok”). Bu kısıtla çok daha iyisini yapabiliriz: Dijkstra, neredeyse doğrusal \(O(V \log V + E)\). (Buradaki \(\log V\) pratikte ~30-60’tan büyük olmaz.)

20.3 2. Gözlem 1: Negatif Olmayan Ağırlık → Mesafe Artar

İlk anahtar gözlem: ağırlıklar \(\geq 0\) ise, en kısa yol boyunca mesafe (zayıf) tekdüze artar.

“If weights greater than or equal to 0, then distances increase along shortest paths.” — Ku, 8:35

Yani \(s \to v\) en kısa yolu bir \(u\)’dan geçiyorsa, \(\delta(s, u) \leq \delta(s, v)\) (aradaki alt-yolun ağırlığı negatif olamaz). Bu, “kaynak merkezli bir küreyi dışa büyütüp önce yakın düğümleri keşfet” stratejisini mümkün kılar. Negatif ağırlık olsaydı, çok uzaktaki bir düğüm negatif kenarla küre içine “sızabilirdi” — bu strateji çökerdi.

Şekil 20.2 bu gözlemi eşmerkezli mesafe kürelerinde gösterir: kaynak \(s\) merkezde, her düğüm \(\delta(s, v)\) yarıçaplı bir kürede; en kısa yol \(s \to c \to a \to b\) daima dışa doğru (\(\delta\): \(0 \to 3 \to 7 \to 9\), kesinlikle artan). Sağdaki karşı-örnek panel, hayali bir \(-8\) kenarı eklenince uzak düğümün iç küreye “sızıp” yapının çöktüğünü — negatif ağırlığın neden yasak olduğunu — gösterir.

Şekil 20.2: Gözlem 1 (Ku L13 §2): ağırlık ≥ 0 → mesafe en kısa yol BOYUNCA ARTAR — eşmerkezli mesafe küreleri sezgisi (L13 §2). SOL: kaynak s merkezde (0,0); yaylar δ = 3, 5, 7, 9 (örnek çizgenin δ değerleri). Her düğüm kendi δ yarıçaplı kürede; en kısa yol s→c→a→b amber, düğüm mesafeleri 0→3→7→9 ARTAN; yol boyunca δ büyür oku. SAĞ — KARŞI-ÖRNEK: aynı küre fikrine b’den iç küreye hayali −8 kenarı eklenirse uzak düğüm mesafesi geri DÜŞER (dış küreden iç küreye SIZAR) → eşmerkezli yapı çöker → açgözlü strateji çöker; negatif ağırlık YASAK gerekçesi. ALT NOT: çıkarılma sırası = artan mesafe s, c, d, a, b; “distances increase along shortest paths” (Ku 8:35). Veri MOTORDAN: dijkstra(s) = {s:0, c:3, d:5, a:7, b:9}; en kısa yol s→c→a→b mesafeleri 0→3→7→9 kesinlikle ARTAN; tüm ağırlıklar ≥ 0.

20.4 3. Gözlem 2: Artan Sıra Bilinirse DAG Relaxation

İkinci gözlem: SSSP’yi daha hızlı çözebiliriz — eğer düğümleri artan mesafe sırasında önceden bilirsek.

“we can solve single source shortest paths faster if we’re given an order of vertices in increasing distance beforehand.” — Ku, 11:22

Neden? Gözlem 1 sayesinde, bu sıraya göre geriye giden her (pozitif) kenar en kısa yola katılamaz → atılır. Geriye yalnız ileri kenarlar kalır → bir DAG → DAG relaxation (doğrusal). (0-ağırlıklı kenarlar istisna: aynı mesafedeki düğümler arası 0-kenar, sırayı çevirerek düzeltilir; 0-ağırlıklı çevrim tek düğüme birleştirilir/coalesce.) Sorun: bu sırayı önceden bilmiyoruz — onu hesaplamamız gerek.

20.5 4. Dijkstra’nın Fikri

Dijkstra (BFS’in ağırlıklı genellemesi; Hollandalı bilgisayar bilimci, “Dijkstra”da J sesi yok — Y’den gelir) iki gözlemi birleştirir:

“Relax edges from vertices in increasing distance from source.” — Ku, 23:09

Kenarları, düğümleri artan mesafe sırasında alarak gevşet. Ama “sıradaki en yakın düğüm” hangisi? Bunu önceden bilmediğimizden, kademeli hesaplarız:

“find the next vertex efficiently using a data structure.” — Ku, 23:47

20.6 5. Değiştirilebilir Öncelik Kuyruğu

Gereken yapı: değiştirilebilir öncelik kuyruğu (changeable priority queue) — üç işlem:

“changeable priority queue has three operations… build… delete min… Decrease the key.” — Ku, 24:42

  • build(items): \(n\) öğeyle kur.
  • delete_min(): en küçük anahtarlı öğeyi çıkar.
  • decrease_key(id, k): belirli id’li öğenin anahtarını daha küçük bir \(k\)’ya düşür.

Fark: her öğenin bir anahtarı (key, mesafe tahmini) ve benzersiz bir id’si (düğüm) vardır. decrease_key, id ile öğeyi bulup anahtarını günceller. İmplementasyon: normal bir öncelik kuyruğu (Ders 12) + bir sözlük (id → kuyruktaki konum) cross-link. Düğüm id’leri \(0 \ldots V-1\) ise, sözlük yerine direct access array\(O(1)\) (hash’in beklenen \(O(1)\)’i yerine kesin \(O(1)\)).

Şekil 20.3 imza şekildir: solda binary min-heap ağacı (her düğümde (key, id)) + altında id→konum cross-link sözlüğü; ortada decrease_key(2, 0) adımı (id 2’nin anahtarı \(8 \to 0\), köke sift-up); sağda üç işlem ve maliyetleri. Çapraz bağ, gevşeyen bir düğümü taramadan \(O(1)\) konumlandırmayı — dolayısıyla \(O(\log n)\) decrease_key’i — mümkün kılar.

Şekil 20.3: Değiştirilebilir öncelik kuyruğu = binary min-heap + id→konum CROSS-LINK sözlüğü → decrease_key O(log n) (L13 §5 İMZA). SOL: 5 düğümlü min-heap ağacı, her düğümde (key, id) çifti + altında pos sözlüğü tablosu (id → heap-konumu satırları); her satırdan ağaçtaki düğüme ince amber cross-link oku (id → konum O(1); D14 işaretçi dersinin akrabası). ORTA: decrease_key(2, 0) adımı — id 2’nin anahtarı 8 üstü çizili → 0, kökten yukarı sift-up okları; pos[2] ile konum O(1) bulundu → O(log n) süzülme. SAĞ: üç işlem kutusu build O(n) / delete_min O(log n) / decrease_key O(log n) (Ku 24:42) + id’ler 0..V−1 ise pos = doğrudan erişim dizisi → kesin O(1) notu. Veri MOTORDAN: ChangeablePQ([(0,5),(1,3),(2,8),(3,1),(4,9)]) heapify sonrası a = [(1,3),(3,1),(8,2),(5,0),(9,4)], is_valid_heap True; decrease_key(2,0) sonra delete_min() == (2,0) (id 2 köke geçti).

20.7 6. Dijkstra Algoritması

Başlat: \(d(s, v) = \infty\) tüm \(v\); \(d(s, s) = 0\). Kuyruğu kur. Boşalana dek: en küçük tahminli \(u\)’yu çıkar, çıkış kenarlarını gevşet (gevşetince decrease_key ile kuyruğu güncelle).

“Dijkstra… designed this very nice generalization of BFS for weighted graphs.” — Ku, 22:02

def dijkstra(adj, weight, s):
    d = {v: float('inf') for v in adj}
    d[s] = 0
    Q = ChangeablePriorityQueue((v, d[v]) for v in adj)   # build
    while Q:                                               # bos degilse
        u = Q.delete_min()                                # en yakin dugum
        for v in adj[u]:
            if d[u] + weight[(u, v)] < d[v]:              # ucgen esitsizligi ihlali
                d[v] = d[u] + weight[(u, v)]              # relax
                Q.decrease_key(v, d[v])                   # kuyrugu guncelle
    return d

Çalışılan Örnek. Pozitif ağırlıklı, çevrimli yönlü çizge. \(s = 0\) ile başla; \(s\)’yi çıkar, \(a \to 10\), \(c \to 3\) gevşet. Kuyruktaki en küçük: \(c\) (3). \(c\)’yi çıkar, \(a \to 7\) (\(3 + 4\)), \(b \to 11\), \(d \to 5\) gevşet. En küçük: \(d\) (5). \(d\)’yi çıkar, \(b \to 10\) (\(5 + 5\)). En küçük: \(a\) (7). \(a\)’yı çıkar, \(b \to 9\) (\(7 + 2\)). Sonra \(b\) (9). Sonuç: \(\delta = \{s{:}0, c{:}3, d{:}5, a{:}7, b{:}9\}\) — doğru en kısa mesafeler.

Şekil 20.4 bu örneğin tam adım izini gösterir: üstte çalışılan çizge (amber kenarlar = en kısa yol ağacı), altta 5 satırlık tablo. Her satır: delete_min edilen düğüm (kesinleşti/donduruldu), gevşetilen kenarlar, kuyrukta kalanlar ve d tablosunun anlık durumu. \(b\) düğümü \(\infty \to 11 \to 10 \to 9\) üç ayrı turda düşer — açgözlü çıkarma sonradan iyileşmeyi engellemez, çıkarılma anında değer kesinleşir.

Şekil 20.4: Dijkstra adım izi: en yakını çıkar · gevşet · decrease_key — çıkarma sırası = ARTAN mesafe (L13 §6 İMZA). ÜST: çalışılan örnek çizge (build_dijkstra_example) sabit yerleşim; düğüm = daire, kenar = yönlü ok + ağırlık rozeti; amber kenarlar = en kısa yol ağacı (gevşetmeyle d’yi belirleyen: s→c, c→a, c→d, a→b). ALT: 5 satırlık adım tablosu (motor order = artan mesafe). Her satır: delete_min edilen düğüm (amber daire + d-değeri + kesinleşti·donduruldu rozeti) · o turda gevşetilen kenarlar (v: eski→yeni, b sütunu ∞→11→10→9 amber vurgu) · kuyruk kalan mini-kutusu · d tablosunun anlık durumu. ALT NOT (Gözlem B): her düğüm BİR kez çıkar, sonra dondurulur — bir daha dokunulmaz; çıkarılma sırası s(0)→c(3)→d(5)→a(7)→b(9). Veri MOTORDAN: dijkstra_trace order = [(s,0),(c,3),(d,5),(a,7),(b,9)]; c relaxed [(a,10,7),(b,∞,11),(d,∞,5)], d [(b,11,10)], a [(b,10,9)]; trace.d == dijkstra == {s:0,a:7,b:9,c:3,d:5}; b evrimi ∞→11→10→9.

20.8 7. Doğruluk: İki Gözlem

İddia: algoritma sonunda tüm \(v\) için \(d(s, v) = \delta(s, v)\). İki gözleme dayanır:

  • Gözlem A: gevşetme bir kez \(d\)’yi gerçek \(\delta\)’ya eşitlerse, sonda da öyle kalır. Çünkü gevşetme yalnız düşürür ve güvenlidir (relaxation safe, Ders 16): tahmin daima gerçek bir yolun ağırlığı veya \(\infty\) — en kısa mesafenin altına inemez.

“relaxation is safe.” — Ku, 41:57

  • Gözlem B: bir düğüm Q’dan çıkarıldığında tahmini zaten \(\delta\)’dır. Tüm düğümler çıkarıldığından, hepsi doğru olur.

“it suffices to show that my estimate equals the shortest distance when v is removed from the Q.” — Ku, 43:02

Çalışılan Örnek — kanıt (tümevarım). Q’dan çıkarılan ilk \(k\) düğüm üzerinde tümevarım. Temel (\(k = 1\)): ilk çıkan \(s\), \(d(s) = 0 = \delta\). Adım: \(v'\), \(k'\). çıkan düğüm; \(s \to v'\) en kısa yolunda, Q’dan henüz çıkmamış ilk düğüm \(y\), öncülü \(x\) ise — \(x\) çıkmış olduğundan (tümevarım) \(d(x) = \delta(x)\), ve \(y\), \(x\)’in en kısa yolundaki ardılı olduğundan \(d(y) = \delta(y)\). Negatif olmayan ağırlık → \(\delta(y) \leq \delta(v')\). Güvenlilik → \(d(v') \geq \delta(v')\). Ve \(v'\) minimum çıkarıldığından \(d(v') \leq d(y) = \delta(y) \leq \delta(v')\). Tüm eşitsizlikler eşitliğe sıkışır → \(d(v') = \delta(v')\). ✓ (Burada negatif olmayan ağırlık şart — Gözlem 1’in özü.)

Şekil 20.5 bu zinciri görselleştirir: üstte \(s \ldots x \to y \ldots v'\) en kısa yol şeması (çıkmış düğümler dolu, kuyruktakiler beyaz), altta sıkışan eşitsizlik zinciri \(\delta(v') \leq d(v') \leq d(y) = \delta(y) \leq \delta(v')\) ve her bağın gerekçesi. Son bağ (\(\delta(y) \leq \delta(v')\)) ağırlık \(\geq 0\)’a dayanır — negatif olsaydı tam burada kırılırdı. Motor, bağımsız bir çapraz kanıt olarak Dijkstra ile Bellman-Ford’un aynı örnekte birebir aynı \(\delta\)’yı verdiğini doğrular.

Şekil 20.5: Dijkstra doğruluğu: SIKIŞAN eşitsizlik zinciri → d(v′) = δ(v′) (L13 §7 İMZA). ÜST ŞERİT: en kısa yol şeması s … x (çıkmış, d=δ rozeti) → y (kuyrukta İLK, d(y)=δ(y)) … v′ (şimdi çıkarılıyor); çıkmış düğümler slate DOLU, kuyruktakiler BEYAZ. ALT: eşitsizlik zinciri büyük kutu δ(v′) ≤ d(v′) ≤ d(y) = δ(y) ≤ δ(v′), her bağın altında gerekçe — güvenli gevşetme / minimum çıkarıldı / tümevarım / AĞIRLIK ≥ 0 (sonuncusu amber: negatifte tam burada çöker); sıkışma çemberi en sol ve en sağ δ(v′) aynı → HEPSİ EŞİT → d(v′) = δ(v′). ALT NOT (Ku 43:02 + 41:57): son bağ y’nin v′ yolunun ön-eki olmasına dayanır (ön-ek ≤ tam yol, ağırlık negatif değilse); negatifte δ(y) > δ(v′) olabilir → zincir KIRILIR. Bağımsız çapraz kanıt — motor iki algoritmayı da çalıştırdı: dijkstra == bellman_ford == {s:0, c:3, d:5, a:7, b:9} BİREBİR.

20.9 8. Çalışma Süresi: Öncelik Kuyruğu Seçimi

Algoritma her şeyi kuyruk işlemleriyle yapar: 1 build (B), \(V\) delete_min (M her biri), \(E\) decrease_key (D her biri):

\[T = B + V \cdot M + E \cdot D\]

Öncelik kuyruğu seçimi sonucu belirler:

  • Array (direct access): delete_min \(O(V)\) (tüm diziyi tara), decrease_key \(O(1)\)\(O(V^2)\). Yoğun çizgede (\(E \sim V^2\)) doğrusal — basit ve iyi.
  • Binary heap (ikili yığın): delete_min \(O(\log V)\), decrease_key \(O(\log V)\) (ebeveynle yukarı swap) → \(O((V+E) \log V) = O(E \log V)\). Seyrek çizgede iyi.
  • Fibonacci heap: \(O(E + V \log V)\) — hem seyrek hem yoğunda en iyi teorik sınır.

“This data structure is called the Fibonacci heap.” — Ku, 55:10

Pratik: Fibonacci heap karmaşıktır, nadiren implemente edilir; çizgenin seyrek/yoğun olduğu bilinirse heap/array yeter. Teori sorusunda \(O(E + V \log V)\) sınırını kullan.

Şekil 20.6 bu üç seçimi tek formül üzerinden karşılaştırır: \(T = B + V \cdot \text{delete\_min} + E \cdot \text{decrease\_key}\) formülüne array / binary heap (amber: derste kullanılan) / Fibonacci heap takılır → \(O(V^2)\) / \(O(E \log V)\) / \(O(E + V \log V)\). Seçim çizgenin yoğunluğuna bağlıdır; teori sorusunda en iyi sınır Fibonacci’nin \(O(E + V \log V)\)’sidir.

Şekil 20.6: PQ seçimi Dijkstra’nın çalışma süresini belirler: T = B + V·delete_min + E·decrease_key (L13 §8 İMZA). ÜST: genel formül panosu (Ku) — B = kuyruğu kur, V kez delete_min, ≤ E kez decrease_key. 3 PQ satırı: (1) ARRAY (doğrudan erişim) delete_min O(V) / decrease_key O(1) → T = O(V²), yoğun çizgede iyi; (2) BINARY HEAP (amber, ★ derste kullanılan, min-heap + cross-link) ikisi de O(log V) → T = O(E log V), seyrek çizgede iyi; (3) FIBONACCI HEAP (amortize) delete_min O(log V)* / decrease_key O(1)* → T = O(E + V log V), teorik en iyi ama pratikte karmaşık nadiren implemente (Ku 55:10). ALT ŞERİT: PQ seçimi çizgenin YOĞUNLUĞUNA bağlı (yoğunda array O(V²), seyrekte binary heap O(E log V)); teori sorusunda en iyi sınır O(E + V log V) kullan. Veri MOTORDAN: dijkstra(s) = {s:0, c:3, d:5, a:7, b:9}; ChangeablePQ = binary heap is_valid_heap True; delete_min sırası ARTAN mesafe s(0) c(3) d(5) a(7) b(9) (Gözlem B).

Şekil 20.7 Dijkstra’nın dayandığı kabulü sentezler: ağırlık \(\geq 0\) kalktığında ne olur. Örnek çizgeye negatif bir \(b \to c\) (\(-8\)) kenarı eklenir (negatif çevrim \(b \to c \to d \to b = -1\)). “Kesinleştir, bir daha dokunma” Dijkstra (\(c\)’yi 3’te dondurur) bu kenarı görmez ve orijinal sonlu yanıtı verir — yanlış; oysa Bellman-Ford negatif çevrimden erişilen herkesi \(-\infty\) işaretler. Bu, Ders 20’deki PS6 (Solomon) Problem 1’in köprüsüdür — orada bu çizge elle adım adım işlenir.

Şekil 20.7: Negatif kenar Dijkstra’yı KIRAR — “kesinleştir, bir daha dokunma” varsayımı çöker (L13 sentez + PS6 köprüsü). SOL: örnek çizge + eklenen b→c(−8) kalın kırık amber kenar; Dijkstra işleme sırası rozetleri (#1..#5); c “d[c]=3’te DONDURULDU (#2)” kilit ikonu; b işlenince (#5) b→c üzerinden 9 + (−8) = 1 < 3 keşfi (kesik amber ok) ama c donmuş → dokunulamaz (YASAK işareti); negatif çevrim b→c→d→b = −8+2+5 = −1; Solomon PS6 4:49 “zamanda donar” notu. SAĞ: karşılaştırma tablosu düğüm / Dijkstra / Bellman-Ford (motor değerleri BİREBİR); fark hücreleri amber (s hariç tüm düğümler). ALT NOT: ağırlık ≥ 0 varsayımı kırılınca Gözlem 1 düşer → kesinleştirme yanlış; çözüm Bellman-Ford (Ders 18); PS6 Problem 1 elle işler (Ders 20 teaser). Veri MOTORDAN: dijkstra(finalize) = {s:0,a:7,b:9,c:3,d:5} (YANLIŞ), bellman_ford_classic = {s:0, a,b,c,d:−∞} (DOĞRU); fark = {a,b,c,d}; donmuş keşif 9+(−8)=1<3; çevrim = −1.

20.10 Bu Dersin Özeti

  1. Dijkstra: negatif olmayan ağırlıklı SSSP; BFS’in genellemesi; ~doğrusal.
  2. Gözlem 1: ağırlık \(\geq 0\) → mesafe en kısa yol boyunca (zayıf) artar.
  3. Gözlem 2: artan sıra bilinirse → DAG relaxation (0-ağırlık birleştir/çevir).
  4. Değiştirilebilir öncelik kuyruğu: build / delete_min / decrease_key (id + key, cross-link sözlük).
  5. Algoritma: \(d = \infty\), \(d(s) = 0\); en yakını çıkar, kenarları gevşet, decrease_key ile güncelle.
  6. Doğruluk: gevşetme güvenli + Q’dan çıkınca \(\delta\); negatif olmayan ağırlık şart.
  7. Süre: \(B + V \cdot M + E \cdot D\) → array \(O(V^2)\) / heap \(O(E \log V)\) / Fibonacci \(O(E + V \log V)\).
ÖnemliTek Bir Cümle

Dijkstra, negatif olmayan ağırlıkta “mesafe en kısa yol boyunca artar” gözlemini kullanır: düğümleri bir öncelik kuyruğundan artan mesafe sırasında çeker, kenarlarını gevşetir, böylece SSSP’yi \(O(E + V \log V)\)’de — BFS’in ağırlıklı genellemesi olarak — çözer.

20.11 Kontrol Soruları

Cevap: Dijkstra, Gözlem 1’e (ağırlık \(\geq 0\) → mesafe en kısa yol boyunca artar) dayanır; bu sayede “en yakın düğümü kesinleştir, bir daha dokunma” açgözlü stratejisi doğrudur. Negatif ağırlık olsaydı, çok uzaktaki bir düğüm negatif bir kenarla beklenenden daha kısa bir yola sahip olabilirdi — yani Q’dan erken çıkarılan bir düğümün mesafesi sonradan daha da kısalabilirdi, kesinleştirme yanlış olurdu. Doğruluk kanıtındaki “\(\delta(y) \leq \delta(v')\)” adımı tam olarak negatif olmayan ağırlığı kullanır; negatif ağırlıkta bu eşitsizlik bozulur. (Negatif için Bellman-Ford gerekir.)

Cevap: Normal öncelik kuyruğu yalnız build ve delete_min sunar; bir öğenin anahtarını sonradan değiştiremezsin. Dijkstra’da bir düğümün mesafe tahmini gevşetmeyle düşer — bu yüzden decrease_key(id, k) gerekir: belirli bir düğümü (id) bulup anahtarını (key) düşürmek. İmplementasyon: normal öncelik kuyruğu + bir sözlük (id → kuyruktaki konum) cross-link; böylece id’den konumu \(O(1)\) bulup yığını güncelleriz. Düğüm id’leri \(0 \ldots V-1\) ise sözlük yerine direct access array → kesin \(O(1)\).

Cevap: Toplam süre \(T = B + V \cdot \text{delete\_min} + E \cdot \text{decrease\_key}\). delete_min ve decrease_key maliyetleri kuyruk implementasyonuna göre değişir: Array → delete_min \(O(V)\), decrease_key \(O(1)\)\(O(V^2)\), yoğun çizgede (\(E \sim V^2\)) en iyi. Binary heap → ikisi de \(O(\log V)\)\(O(E \log V)\), seyrek çizgede en iyi. Fibonacci heap\(O(E + V \log V)\), her durumda en iyi teorik ama pratikte karmaşık. Yani “en hızlı” çizgenin yoğunluğuna bağlıdır; teori sorusunda \(O(E + V \log V)\) sınırı verilir.

Cevap: Sıralama, “geriye giden kenar atılır” mantığına dayanır; ama 0-ağırlıklı bir kenar iki düğümü aynı mesafede bırakır, yani sırada hangisinin önce geldiği belirsizdir ve “geriye kenar” olabilir. Çözüm: aynı mesafedeki düğümleri 0-ağırlıklı kenara göre yeniden sırala (kenar ileri gidecek şekilde); bir 0-ağırlıklı çevrim varsa, o çevrimdeki tüm düğümleri tek bir düğüme birleştir (coalesce) — birine ulaşmak hepsine ulaşmaktır. Böylece çizge geçerli bir topolojik sıraya ve DAG’a iner.

20.12 Egzersizler

Egzersiz 1. Küçük bir pozitif-ağırlıklı çizgede Dijkstra’yı elle çalıştır: her adımda Q’dan çıkan düğümü, gevşetilen kenarları ve güncel \(d\) değerlerini yaz.

Egzersiz 2. Değiştirilebilir öncelik kuyruğunu binary heap + direct access array ile Python’da implemente et (build / delete_min / decrease_key); decrease_key’in \(O(\log V)\) olduğunu doğrula.

Egzersiz 3. Aynı çizgede Dijkstra’yı array, binary heap ve (kavramsal) Fibonacci heap sınırlarıyla karşılaştır; çizge seyrek/yoğun iken hangisinin kazandığını göster.

Egzersiz 4. Bir negatif kenarlı küçük çizge bul; Dijkstra’nın neden yanlış sonuç verdiğini, hangi düğümün erken kesinleştiğini adım adım göster.

Egzersiz 5. Dijkstra’ya ebeveyn işaretçisi ekle (gevşetirken \(P(v) = u\)); algoritma sonunda en kısa yol ağacını yeniden kur.

20.13 Sonraki Ders İçin Hazırlık

UyarıSonraki: Ders 21 (L14) — Tüm-Çiftler En Kısa Yollar (APSP)

Ders 21 (L14): Tüm-Çiftler En Kısa Yollar (APSP) — Jason Ku ile, tek kaynak yerine tüm düğüm çiftleri arasında en kısa yol. (Araya Ders 20 = PS6 problem oturumu girer.) Naif yol: her düğümden Dijkstra/Bellman-Ford. Daha akıllısı Johnson algoritması: negatif ağırlıkları, bir potansiyel fonksiyonla negatif-olmayana “yeniden ağırlıklandırıp” Dijkstra’yı \(V\) kez çalıştırmak.

Ders 21 Öncesi Yapılacak:

  • Bu dersin egzersizlerini, özellikle Egzersiz 1 (elle Dijkstra) ve 2 (değiştirilebilir PQ) çöz.
  • Üç çalışma süresi sınırını (array/heap/Fibonacci) ve hangi çizgede hangisi olduğunu ezberle.
  • Ana cümleyi tekrar oku: “Negatif yoksa mesafe artar; en yakını çek, gevşet, decrease_key.”

20.14 Anahtar Kavramlar (Cheat Sheet)

Kavram Tanım Sayfada
Dijkstra kapsamı Negatif olmayan ağırlıklı SSSP; BFS genellemesi Böl. 1
Gözlem 1 Ağırlık \(\geq 0\) → mesafe en kısa yol boyunca artar Böl. 2
Gözlem 2 Artan sıra bilinirse → DAG relaxation Böl. 3
Değiştirilebilir PQ build / delete_min / decrease_key (id + key) Böl. 5
Dijkstra döngüsü En yakını çıkar → kenarları gevşet → decrease_key Böl. 6
Doğruluk Gevşetme güvenli + Q’dan çıkınca \(\delta\); negatif yok şart Böl. 7
Array PQ \(O(V^2)\); yoğun çizgede iyi Böl. 8
Heap / Fibonacci \(O(E \log V)\) / \(O(E + V \log V)\) Böl. 8

20.15 Builder ve OMSCS Bağlantıları

İpucu6 köprü

Bu ders, “negatif yoksa mesafe artar” gözlemiyle açgözlü SSSP’yi kurar ve değiştirilebilir öncelik kuyruğu ile neredeyse doğrusal süreye iner — köprülerin özeti:

  1. Dijkstra → GPS/yönlendirme (Google Maps, Waze), ağ link-state (OSPF), oyun yol bulma.
  2. Sezgisel arama → Dijkstra + heuristic = A*; robot hareket planlama, video oyunu NPC, harita rotalama.
  3. Değiştirilebilir öncelik kuyruğu → olay simülasyonu (event queue), zamanlayıcı, Prim MST.
  4. Açgözlü + güvenli gevşetme → açgözlü algoritma kanıt deseni (exchange argument).
  5. PQ veri yapısı seçimi → seyrek/yoğun bilinciyle karmaşıklık ayarı; gerçek sistemde profil-temelli seçim.
  6. Fibonacci heap → teorik optimal vs pratik basitlik; “asimptotik en iyi her zaman pratik en iyi değildir”.

ÖnemliTek bir şey alıp gideceksen

Dijkstra’nın sırrı tek bir gözlemdir — negatif ağırlık yoksa mesafe en kısa yol boyunca artar, dolayısıyla “en yakın düğümü kesinleştir, bir daha dokunma” açgözlü stratejisi doğrudur. Düğümleri bir öncelik kuyruğundan artan mesafe sırasında çekip kenarlarını gevşeterek SSSP’yi neredeyse doğrusal \(O(E + V \log V)\)’de çözeriz. Bu, BFS’in ağırlıklı dünyaya taşınmış hâlidir.