Bölüm 4.1: Çizge Gezinme Algoritmaları
Giriş
Öğrenme Çıktıları
Yönlü, yönsüz, ağırlıklı, iki parçalı, tam çizge ve ağaç türlerini ayırt etmek; komşuluk listesi ve matrisiyle göstermek.
Derinlik öncelikli aramayı özyinelemeli ve yığınla yazmak, ziyaret sırasını adım adım çıkarmak, geri izlemeyi açıklamak.
Genişlik öncelikli aramayı kuyrukla yazmak; katman, ata ve ağırlıksız en kısa yol kavramlarını labirent örneğinde uygulamak.
Güçlü bağlantılı bileşenleri indeks / enDüşükBağ değerleriyle bulmak; tüm gezinme algoritmalarının O(V + E) zamanını gerekçelendirmek.
Giriş
Çizgeler Her Yerde
- Dünya bir ağ gibidir: şehirler yollarla, insanlar ilişkilerle, bilgisayarlar kablolarla birbirine bağlıdır.
- Çizge (graph) G = (V, E): düğümler (vertex, V) ve onları birleştiren kenarlar (edge, E).
- Çizge algoritmaları bu ağları inceler ve anlamlandırır: en kısa yol, gruplama, bağlantılılık, akış…
- Kullanım: sosyal ağlar, harita ve navigasyon, ağ yönlendirme, derleyiciler, oyunlar.
Şehirler düğüm, yollar kenar.
Giriş
Çizge Türleri
Kenarlar yalnızca ok yönünde geçilir.
Kenarlar iki yönde de geçilir.
Her kenara bir ağırlık (maliyet, uzaklık) atanır.
Kenarlar yalnızca farklı kümelerdeki düğümleri bağlar.
A–D–C–B–A gibi başladığı yere dönen bir yol vardır.
Bağlı ve döngüsüz; iki düğüm arasında tek yol.
Her düğüm çifti bir kenarla bağlı: n(n−1)/2 kenar.
Çoklu kenar ve öz döngü içerir; basit çizge içermez.
Giriş
Çizge Algoritmalarının Çeşitleri
DFS bir düğümden başlayıp dallanarak, BFS katman katman tüm ağı gezer. Bu bölüm.
Dijkstra: başlangıçtan diğer tüm düğümlere en kısa yollar. Bölüm 4.2.
Kruskal: ağı minimum toplam maliyetle bağlayan kenarları seçer. Bölüm 4.3.
Kapasiteli bir ağda kaynaktan hedefe gönderilebilecek en büyük akış. Bölüm 4.4.
| Algoritma | Günlük örnek |
|---|---|
| DFS | Bir labirentten çıkış yolu ararken bir koridoru sonuna kadar izleyip geri dönmek |
| BFS | Bir haberin şehirde kişiden kişiye yayılması: önce yakın çevre, sonra onların çevresi |
| Dijkstra | Bir kuryenin en kısa sürede teslimat yapacağı rotayı bulmak |
Giriş
Çizgeyi Bellekte Tutmak
V = 4, E = 4
Komşuluk listesi
0: 1, 2 1: 0, 2 2: 0, 1, 3 3: 2
Komşuluk matrisi
| 0 | 1 | 2 | 3 | |
|---|---|---|---|---|
| 0 | 0 | 1 | 1 | 0 |
| 1 | 1 | 0 | 1 | 0 |
| 2 | 1 | 1 | 0 | 1 |
| 3 | 0 | 0 | 1 | 0 |
| Komşuluk listesi | Komşuluk matrisi | |
|---|---|---|
| Bellek | O(V + E) | O(V²) |
| u'nun tüm komşuları | O(derece(u)) | O(V) |
| (u, v) kenarı var mı? | O(derece(u)) | O(1) |
| Gezinme (DFS / BFS) | O(V + E) | O(V²) |
Bu bölümdeki Java kodları komşuluk listesi kullanır: List<Integer>[] komsu.
Gezinme
Çizge Gezinme
- Gezinme (graph traversal), çizgenin yapısını sistematik biçimde keşfetmek için her düğümü (ulaşılabiliyorsa) tam bir kez ziyaret eder.
- İki ana yaklaşım vardır; fark, sıradaki düğümün hangi veri yapısından alındığıdır:
Yığıt (stack, LIFO) kullanır. Bir düğümden başlar, mümkün olduğunca derine iner; komşuları bitince geri döner (backtracking).
Kuyruk (queue, FIFO) kullanır. Başlangıca uzaklığı 1 olanları, sonra 2 olanları… seviye seviye ziyaret eder.
ziyaret[] işaretlenmezse algoritma aynı döngüde sonsuza kadar dolaşır.DFS
DFS: Fikir
- Labirentte bir koridora gir, çıkmaza kadar ilerle; çıkmazda son yol ayrımına geri dön ve henüz denenmemiş koridoru seç.
- Özyinelemeli sürümde geri dönüş noktalarını çağrı yığını hatırlar; yinelemeli sürümde açık bir yığıt kullanılır.
- Ziyaret edilen düğümler ve onlara ilk ulaşılan kenarlar bir DFS ağacı oluşturur.
- Ziyaret sırası komşuların sırasına bağlıdır; aynı çizgede farklı ama geçerli DFS sıraları vardır.
beyaz: henüz keşfedilmedi
sarı: keşfedildi, işi sürüyor (yığında)
koyu: bitti, tüm komşuları incelendi
DFS · Adım adım
DFS Adım Adım: Ziyaret Sırası
Başlangıç: hiçbir düğüm ziyaret edilmedi. DFS kökten başlayacak; düğümlere ziyaret sırası numarası yazılacak.
Kök ziyaret edilir (1). Komşulardan ilki seçilip derine inilecek.
1 düğümünün ilk keşfedilmemiş komşusuna inilir: 2. Yığın derinleşir.
2 düğümünün ilk keşfedilmemiş komşusuna inilir: 3. Yığın derinleşir.
3 düğümünün ilk keşfedilmemiş komşusuna inilir: 4. Yığın derinleşir.
4, 3, 2 düğümlerinin keşfedilmemiş komşusu kalmadı → geri dönüldü (backtrack). 1 düğümünün sıradaki çocuğu ziyaret edilir: 5.
5 düğümünün ilk keşfedilmemiş komşusuna inilir: 6. Yığın derinleşir.
6 düğümünün ilk keşfedilmemiş komşusuna inilir: 7. Yığın derinleşir.
7, 6 düğümlerinin keşfedilmemiş komşusu kalmadı → geri dönüldü (backtrack). 5 düğümünün sıradaki çocuğu ziyaret edilir: 8.
8, 5 düğümlerinin keşfedilmemiş komşusu kalmadı → geri dönüldü (backtrack). 1 düğümünün sıradaki çocuğu ziyaret edilir: 9.
9 düğümünün ilk keşfedilmemiş komşusuna inilir: 10. Yığın derinleşir.
Tüm düğümler ziyaret edildi; yığın boşaldı. Ziyaret sırası: 1, 2, 3, …, 10: her dal sonuna kadar izlenip sonra geri dönüldü.
DFS
Özyinelemeli DFS: Sözde Kod
İngilizce sözde kod
procedure DFS(G, v) is label v as discovered for all directed edges from v to w that are in G.adjacentEdges(v) do if vertex w is not labeled as discovered then call DFS(G, w)
Türkçe
DFS(düğüm, ziyaretEdilenler): eğer düğüm ziyaretEdilenler içinde değilse: düğüm'ü ziyaretEdilenler'e ekle düğüm'ü işle (örneğin, yazdır) her bir komşu düğüm için: DFS(komşu, ziyaretEdilenler)
- Soldaki sürüm komşuyu çağırmadan önce, sağdaki çağrının başında denetler; ikisi aynı sırayı üretir. Sağdakinde gereksiz çağrılar yapılır ama hemen döner.
- Bağlı olmayan bir çizgede tüm düğümleri gezmek için DFS, ziyaret edilmemiş her düğümden yeniden başlatılır (her başlatma bir bağlı bileşen).
DFS
Yinelemeli DFS: Sözde Kod
İngilizce sözde kod
procedure DFS_iterative(G, v) is let S be a stack S.push(v) while S is not empty do v = S.pop() if v is not labeled as discovered then label v as discovered for all edges from v to w in G.adjacentEdges(v) do S.push(w)
Türkçe
DFS(başlangıç_düğüm): boş bir yığın oluştur başlangıç_düğüm'ü yığına ekle döngü yığın boş değilken: mevcut_düğüm = yığından çıkar eğer mevcut_düğüm ziyaretEdilenler içinde değilse: mevcut_düğüm'ü ziyaretEdilenler'e ekle her bir komşu_düğüm için: komşu_düğüm'ü yığına ekle
- Özyineleme yerine açık bir yığın: çok derin çizgelerde
StackOverflowErrorriski yoktur. - İşaretleme çekerken yapılır; bu yüzden bir düğüm yığına birden çok kez girebilir (yığın boyutu O(E)).
- Komşular itildiği için en son itilen önce çıkar: sıra, özyinelemeli sürümün tersine döner. Aynı sıra istenirse komşular ters sırada itilir.
DFS · Kodu adım adım çalıştır
Özyinelemeli DFS: dfs(0) İzleme
Örnek yönlü çizge: 0→1, 0→2, 0→7, 1→2, 2→0, 2→3, 2→4, 3→5, 4→6, 5→6. dfs(0): 0 işaretlenir, yazdırılır.
0'ın komşusu 1 ziyaret edilmemiş → dfs(1): işaretlenir, yazdırılır. Yığına yeni çerçeve eklendi.
1'in komşusu 2 ziyaret edilmemiş → dfs(2): işaretlenir, yazdırılır. Yığına yeni çerçeve eklendi.
2'nin komşusu 0 zaten ziyaret edildi → atlanır (döngüye girilmez).
2'nin komşusu 3 ziyaret edilmemiş → dfs(3): işaretlenir, yazdırılır. Yığına yeni çerçeve eklendi.
3'ün komşusu 5 ziyaret edilmemiş → dfs(5): işaretlenir, yazdırılır. Yığına yeni çerçeve eklendi.
5'in komşusu 6 ziyaret edilmemiş → dfs(6): işaretlenir, yazdırılır. Yığına yeni çerçeve eklendi.
6, 5, 3 bitti (komşu kalmadı) → çağrı(lar) döner, kontrol 2'ye geri gelir: geri izleme.
2'nin komşusu 4 ziyaret edilmemiş → dfs(4): işaretlenir, yazdırılır. Yığına yeni çerçeve eklendi.
4'ün komşusu 6 zaten ziyaret edildi → atlanır (döngüye girilmez).
4, 2, 1 bitti (komşu kalmadı) → çağrı(lar) döner, kontrol 0'a geri gelir: geri izleme.
0'ın komşusu 2 zaten ziyaret edildi → atlanır (döngüye girilmez).
0'ın komşusu 7 ziyaret edilmemiş → dfs(7): işaretlenir, yazdırılır. Yığına yeni çerçeve eklendi.
7, 0 bitti; dfs(0) da döner. Çıktı: 0 1 2 3 5 6 4 7. Kalın kenarlar DFS ağacını oluşturur.
void dfs(int v, boolean[] ziyaret) { ziyaret[v] = true; System.out.print(v + " "); for (int w : komsu[v]) if (!ziyaret[w]) dfs(w, ziyaret); }
DFS · Kodu adım adım çalıştır
Yığınla DFS: dfsYigin(0) İzleme
Başlangıç düğümü 0 yığına itilir. Henüz hiçbir düğüm işaretli değil.
0 çekildi: işaretlenir, yazdırılır; komşuları 1, 2, 7 sırayla itilir → en son itilen 7 en üstte.
7 çekildi: işaretlenir, yazdırılır; komşusu yok, yığına bir şey eklenmez.
2 çekildi: işaretlenir, yazdırılır; komşuları 0, 3, 4 sırayla itilir → en son itilen 4 en üstte.
4 çekildi: işaretlenir, yazdırılır; komşusu 6 itilir → en son itilen 6 en üstte.
6 çekildi: işaretlenir, yazdırılır; komşusu yok, yığına bir şey eklenmez.
3 çekildi: işaretlenir, yazdırılır; komşusu 5 itilir → en son itilen 5 en üstte.
5 çekildi: işaretlenir, yazdırılır; komşusu 6 itilir → en son itilen 6 en üstte.
6 çekildi ama zaten ziyaret edilmiş → hiçbir şey yapılmaz. (Bir düğüm yığına birden çok kez girebilir.)
0 çekildi ama zaten ziyaret edilmiş → hiçbir şey yapılmaz. (Bir düğüm yığına birden çok kez girebilir.)
1 çekildi: işaretlenir, yazdırılır; komşusu 2 itilir → en son itilen 2 en üstte.
2 çekildi ama zaten ziyaret edilmiş → hiçbir şey yapılmaz. (Bir düğüm yığına birden çok kez girebilir.)
Yığın boş, döngü biter. Çıktı: 0 7 2 4 6 3 5 1: özyinelemeli sürümden (0 1 2 3 5 6 4 7) farklı, çünkü komşular ters sırada çıkar.
void dfsYigin(int s) { boolean[] ziyaret = new boolean[n]; Deque<Integer> yigin = new ArrayDeque<>(); yigin.push(s); while (!yigin.isEmpty()) { int v = yigin.pop(); if (!ziyaret[v]) { ziyaret[v] = true; System.out.print(v + " "); for (int w : komsu[v]) yigin.push(w); } } }
DFS
DFS: Analiz ve Kullanım
Karmaşıklık
- Her düğüm bir kez işaretlenir: O(V).
- Her düğümün komşu listesi bir kez taranır; listelerin toplam uzunluğu E (yönsüzde 2E): O(E).
- Toplam: O(V + E) (komşuluk listesi), O(V²) (matris: her düğüm için V elemanlı satır).
- Ek bellek:
ziyaret[]O(V) + yığın: özyinelemede en uzun yol kadar, O(V); "çekerken işaretle" sürümünde O(E).
Nerede kullanılır?
- Ulaşılabilirlik, bağlı bileşen sayısı
- Döngü tespiti: yığındaki (sarı) bir düğüme giden kenar = geri kenar
- Topolojik sıralama: bitiş sırasının tersi
- Güçlü bağlantılı bileşenler (Tarjan, Kosaraju)
- Köprü ve eklem noktaları, labirent ve bulmaca çözme, geri izleme (backtracking)
BFS
BFS: Fikir
- Suya atılan taşın dalgası gibi: önce başlangıca 1 kenar uzaktakiler, sonra 2 kenar uzaktakiler…
- Bir haberin yayılmasını modeller: kişi haberi tüm arkadaşlarına söyler, onlar da kendi arkadaşlarına.
- Kuyruk (FIFO) sırayı korur: önce keşfedilen önce işlenir.
- Düğüm kuyruğa girerken işaretlenir; böylece her düğüm kuyruğa en fazla bir kez girer.
- Ağırlıksız çizgede başlangıçtan her düğüme en az kenarlı yolu bulur.
L0 = {s}
L1 = s'nin komşuları
L2 = L1'in yeni komşuları
…
Lk'deki her düğümün s'ye uzaklığı tam k'dir.
BFS · Adım adım
BFS Adım Adım: Katman Katman
Başlangıç: tüm düğümler beyaz (keşfedilmedi). Sarı = keşfedildi, kuyrukta bekliyor · koyu = işlendi.
Başlangıç düğümü 1 keşfedildi olarak işaretlenir ve kuyruğa konur.
Kuyruktan 1 alınır; keşfedilmemiş komşuları 2, 3, 4, 5 işaretlenip kuyruğa eklenir (1. katman).
Kuyruktan 2 alınır; keşfedilmemiş komşuları 6, 7 işaretlenip kuyruğa eklenir (2. katman).
Kuyruktan 3 alınır; keşfedilmemiş komşusu 8 işaretlenip kuyruğa eklenir (2. katman).
Kuyruktan 4 alınır; keşfedilmemiş komşusu yok.
Kuyruktan 5 alınır; keşfedilmemiş komşusu yok.
Kuyruktan 6 alınır; keşfedilmemiş komşusu yok.
Kuyruktan 7 alınır; keşfedilmemiş komşusu yok.
Kuyruktan 8 alınır; keşfedilmemiş komşusu yok → kuyruk boşaldı, BFS biter.
Sonuç: ziyaret sırası 1, 2, 3, 4, 5, 6, 7, 8. Önce uzaklığı 1 olanlar (2, 3, 4, 5), sonra uzaklığı 2 olanlar (6, 7, 8): katman katman.
BFS
BFS: Sözde Kod
İngilizce sözde kod
procedure BFS(G, root) is let Q be a queue label root as explored Q.enqueue(root) while Q is not empty do v := Q.dequeue() for all edges from v to w in G.adjacentEdges(v) do if w is not labeled as explored then label w as explored w.parent := v Q.enqueue(w)
Türkçe
BFS(başlangıç_düğüm): boş bir kuyruk oluştur başlangıç_düğüm'ü keşfedildi işaretle başlangıç_düğüm'ü kuyruğa koy döngü kuyruk boş değilken: mevcut_düğüm = kuyruktan al her bir komşu_düğüm için eğer komşu_düğüm keşfedilmemiş ise komşu_düğüm'ü keşfedildi işaretle komşu_düğüm'ün atası = mevcut_düğüm komşu_düğüm'ü kuyruğa koy
w.parent := v(ata) bağlantıları BFS ağacını oluşturur; hedeften köke doğru izlenince en kısa yol elde edilir.- İşaretleme kuyruğa eklerken yapılır, çıkarırken değil: aksi hâlde aynı düğüm kuyruğa defalarca girer.
BFS · Kodu adım adım çalıştır
BFS: bfs(0) İzleme
0 işaretlenir ve kuyruğa eklenir. Kuyruk yalnızca 0'ı içerir.
Kuyruğun önünden 0 alınır ve yazdırılır.
0'ın komşuları: keşfedilmemiş komşular 1, 2, 7 işaretlenip kuyruğun arkasına eklenir.
Kuyruğun önünden 1 alınır ve yazdırılır.
1'in komşuları: 2 zaten işaretli → atlanır.
Kuyruğun önünden 2 alınır ve yazdırılır.
2'nin komşuları: keşfedilmemiş komşular 3, 4 işaretlenip kuyruğun arkasına eklenir; 0 zaten işaretli → atlanır.
Kuyruğun önünden 7 alınır ve yazdırılır.
Kuyruğun önünden 3 alınır ve yazdırılır.
3'ün komşuları: keşfedilmemiş komşu 5 işaretlenip kuyruğun arkasına eklenir.
Kuyruğun önünden 4 alınır ve yazdırılır.
4'ün komşuları: keşfedilmemiş komşu 6 işaretlenip kuyruğun arkasına eklenir.
Kuyruğun önünden 5 alınır ve yazdırılır.
5'in komşuları: 6 zaten işaretli → atlanır.
Kuyruğun önünden 6 alınır ve yazdırılır.
Kuyruk boş. Çıktı: 0 1 2 7 3 4 5 6, katman katman: {0}, {1, 2, 7}, {3, 4}, {5, 6}. Kalın kenarlar BFS ağacıdır.
void bfs(int s) { boolean[] ziyaret = new boolean[n]; Queue<Integer> kuyruk = new LinkedList<>(); ziyaret[s] = true; kuyruk.add(s); while (!kuyruk.isEmpty()) { int v = kuyruk.poll(); System.out.print(v + " "); for (int w : komsu[v]) if (!ziyaret[w]) { ziyaret[w] = true; kuyruk.add(w); } } }
BFS · Adım adım
BFS ile Labirent: Tüm Yolları Eşzamanlı Dene
Giriş hücresi uzaklık 0 ile kuyruğa konur. Duvarlar (koyu) çizgede düğüm değildir; her boş hücre bir düğüm, komşu boş hücreler kenardır.
Dalga her adımda tüm yönlere bir hücre ilerler: uzaklığı 1 ve 2 olan hücreler. Sarı = şu anki sınır (frontier).
Tek koridor: aşağı inilir. Her hücreye yazılan sayı, girişten oraya en az adım sayısıdır.
Uzaklık 5'te yol çatallanır: dalga hem sağa hem aşağı yayılır. BFS "var olan tüm yolları eşzamanlı dener".
İki kol aynı anda ilerler: biri yukarıdaki odaya, biri aşağıya.
Uzaklık 10: dalga yukarıdaki koridora ve aşağıda alt koridora ulaştı.
Alt koridor çıkışa yaklaşıyor; üst kol çıkmaz sokaklarda dolaşıyor.
Çıkış hücresine 14 adımda ulaşıldı. BFS ilk ulaştığı anda bu uzaklık kesin olarak en kısadır.
Ata (parent) bağlantıları çıkıştan geriye izlenerek en kısa yol (yeşil, 15 hücre, 14 adım) elde edilir. Kalan hücreler (uzaklık 15–17) bu yol için gereksizdi.
BFS
BFS Ağacı ve En Kısa Yol
| düğüm | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|---|---|---|---|---|---|---|---|---|
| uzaklık | 0 | 1 | 1 | 1 | 1 | 2 | 2 | 2 |
| ata | — | 1 | 1 | 1 | 1 | 2 | 2 | 3 |
"BFS Adım Adım" çizgesi, kaynak 1. Yol(8): 8 → ata 3 → ata 1, ters çevrilince 1 → 3 → 8 (2 kenar).
- Keşifte:
uzaklik[w] = uzaklik[v] + 1; ata[w] = v; - Kuyruktan çıkan uzaklıklar azalmayan sıradadır (0, 1, 1, 1, 1, 2, 2, 2); bu yüzden bir düğüme ilk ulaşan yol en kısadır.
Yolu geri kurma
List<Integer> yol(int[] ata, int s, int t) { LinkedList<Integer> yol = new LinkedList<>(); for (int v = t; v != -1; v = ata[v]) yol.addFirst(v); // hedeften geriye if (yol.getFirst() != s) return null; // t'ye ulaşılamadı return yol; }
BFS
BFS ve DFS Karşılaştırması
| DFS | BFS | |
|---|---|---|
| Yardımcı yapı | Yığın (LIFO) / özyineleme | Kuyruk (FIFO) |
| Gezinme biçimi | bir dalı sonuna kadar, sonra geri dönüş | katman katman (uzaklık sırasıyla) |
| Zaman | O(V + E) liste · O(V²) matris | O(V + E) liste · O(V²) matris |
| Ek bellek | O(V): en uzun yol yığında | O(V): en geniş katman kuyrukta |
| En kısa yol | hayır | evet (ağırlıksız çizgede) |
| Ürettiği ağaç | derin, ince DFS ağacı | sığ, geniş BFS ağacı |
| Tipik kullanım | döngü, bileşen, topolojik sıra, SCC, geri izleme | en az adım, seviye, iki parçalılık testi, yayılma |
BFS
Deneyin: BFS ve DFS
Yönsüz çizge; komşular alfabetik sırayla gezilir. DFS özyinelemeli sürümün sırasını verir (yığına komşular ters sırada itilir). Sarı: kuyrukta / yığında · turuncu çerçeve: şu an işlenen · koyu: bitti.
Tarjan SCC
Güçlü Bağlantılı Bileşenler
- Yönlü çizgede u ve v karşılıklı ulaşılabilir: u'dan v'ye ve v'den u'ya yol var.
- Güçlü bağlantılı bileşen (strongly connected component, SCC): her düğümün diğer her düğüme ulaşabildiği maksimal düğüm kümesi.
- Her düğüm tam bir SCC'ye aittir; tek düğüm de bir SCC olabilir ({8}).
- Dikkat: "bir düğüm ve ondan erişilebilen tüm düğümler" SCC değildir: 8'den herkese ulaşılır ama kimse 8'e ulaşamaz.
- Tarjan algoritması (1972) tüm SCC'leri tek bir DFS ile bulur.
SCC'ler: {1, 2, 3}, {4, 5}, {6, 7}, {8}
Tarjan SCC
Tarjan: İlkeler ve Adımlar
- DFS tabanlıdır: çizgenin tüm düğümleri sırayla denenir; ziyaret edilmemiş her düğümden DFS başlatılır.
- DFS sırasında ziyaret edilen her düğüme bir indeks atanır: düğümün keşif sırası (1, 2, 3, …). Mevcut DFS yolundaki ve SCC'si henüz belirlenmemiş düğümler bir yığında tutulur.
- Her düğüm için enDüşükBağ (low-link) hesaplanır: düğümün DFS alt ağacından, en fazla bir kenarla hâlâ yığında olan düğümlere ulaşarak erişilebilen en küçük indeks.
- Ağaç kenarı v → w: dönüşte
enDüşükBağ(v) = min(enDüşükBağ(v), enDüşükBağ(w)) - Yığındaki w'ye kenar:
enDüşükBağ(v) = min(enDüşükBağ(v), indeks(w)) - Bitmiş bir SCC'deki w'ye kenar: yok sayılır.
- Ağaç kenarı v → w: dönüşte
- v'nin işi bitince enDüşükBağ(v) = indeks(v) ise v bir SCC'nin köküdür: yığından v'ye kadar (v dahil) çekilen düğümler bir SCC oluşturur.
Tarjan SCC · Adım adım
Tarjan Adım Adım: indeks, enDüşükBağ
Yönlü çizge, 8 düğüm. Her düğümün yanına indeks,enDüşükBağ yazılacak. Beyaz = ziyaret edilmedi, sarı = yığında, renkli = SCC'si belirlendi.
SCC(1): indeks = enDüşükBağ = 1; 1 yığına itilir.
SCC(2): indeks = enDüşükBağ = 2; 2 yığına itilir.
SCC(3): indeks = enDüşükBağ = 3; 3 yığına itilir.
Kenar 3 → 1: 1 yığında → enDüşükBağ(3) = min(3, indeks(1) = 1) = 1.
3 bitti; enDüşükBağ 1 < indeks 3 → kök değil, yığında kalır. 2'ye dönüldü: enDüşükBağ(2) = min(2, enDüşükBağ(3) = 1) = 1.
2 bitti; enDüşükBağ 1 < indeks 2 → kök değil, yığında kalır. 1'e dönüldü; enDüşükBağ(1) = 1 değişmedi. 1'de enDüşükBağ = indeks = 1 → 1 köktür: yığından 1'e kadar çekilenler bir SCC: {1, 2, 3}.
SCC(4): indeks = enDüşükBağ = 4; 4 yığına itilir.
4 → 2 yok sayılır (2 bitmiş bir SCC'de, yığında değil). 4 → 3 yok sayılır (3 bitmiş bir SCC'de, yığında değil). SCC(5): indeks = enDüşükBağ = 5; 5 yığına itilir.
SCC(6): indeks = enDüşükBağ = 6; 6 yığına itilir.
6 → 3 yok sayılır (3 bitmiş bir SCC'de, yığında değil). SCC(7): indeks = enDüşükBağ = 7; 7 yığına itilir.
Kenar 7 → 6: 6 yığında → enDüşükBağ(7) = min(7, indeks(6) = 6) = 6.
7 bitti; enDüşükBağ 6 < indeks 7 → kök değil, yığında kalır. 6'ya dönüldü; enDüşükBağ(6) = 6 değişmedi. 6'da enDüşükBağ = indeks = 6 → 6 köktür: yığından 6'ya kadar çekilenler bir SCC: {6, 7}.
5'e dönüldü; enDüşükBağ(5) = 5 değişmedi. Kenar 5 → 4: 4 yığında → enDüşükBağ(5) = min(5, indeks(4) = 4) = 4.
5 bitti; enDüşükBağ 4 < indeks 5 → kök değil, yığında kalır. 4'e dönüldü; enDüşükBağ(4) = 4 değişmedi. 4'te enDüşükBağ = indeks = 4 → 4 köktür: yığından 4'e kadar çekilenler bir SCC: {4, 5}.
SCC(8): indeks = enDüşükBağ = 8; 8 yığına itilir.
8 → 5 yok sayılır (5 bitmiş bir SCC'de, yığında değil). 8 → 7 yok sayılır (7 bitmiş bir SCC'de, yığında değil). Öz döngü 8 → 8: 8 yığında ama enDüşükBağ = min(8, 8) = 8 değişmez.
8'de enDüşükBağ = indeks = 8 → 8 köktür: yığından 8'e kadar çekilenler bir SCC: {8}.
Sonuç: 4 SCC. Her SCC, kökü (indeks = enDüşükBağ olan düğüm: 1, 6, 4, 8) bittiği anda bulundu; bulunma sırası ters topolojik sıradır.
Tarjan SCC
Tarjan: Sözde Kod
TARJAN_SCC(G): indeks = 1 yığın = [ ] SCCler = [ ] her bir v için V içinde: eğer v.indeks tanımlı değilse: SCC(v) döndür SCCler
SCC(v): v.indeks = v.enDüşükBağ = indeks++ yığın.ekle(v) her bir (v, w) için E içinde: eğer w.indeks tanımlı değilse: SCC(w) v.enDüşükBağ = min(v.enDüşükBağ, w.enDüşükBağ) değilse eğer w yığında ise: v.enDüşükBağ = min(v.enDüşükBağ, w.indeks) eğer v.enDüşükBağ == v.indeks: scc = [ ] döngü w ≠ v olduğu sürece: // en az bir kez w = yığın.çıkar() scc.ekle(w) SCCler.ekle(scc)
- Görselde indeks 1'den başlatıldı (
indeks = 1); 0'dan başlatmak da aynı SCC'leri verir. - "yığında mı?" sorusu, yığına paralel bir
yiginda[]dizisiyle O(1) zamanda yanıtlanır.
Tarjan SCC · Kodu adım adım çalıştır
Tarjan: scc(0) İzleme
Çizge: 0→1, 1→2, 2→0, 2→3, 3→4, 4→3; indeks[w] == 0 = ziyaret edilmedi. scc(0): indeks ve dusuk = 1; 0 yığına itilir.
0 → 1: 1 ziyaret edilmemiş → özyinelemeli çağrı.
scc(1): indeks ve dusuk = 2; 1 yığına itilir.
1 → 2: 2 ziyaret edilmemiş → özyinelemeli çağrı.
scc(2): indeks ve dusuk = 3; 2 yığına itilir.
2 → 0: 0 yığında (geri kenar) → dusuk[2] = min(3, indeks[0] = 1) = 1.
2 → 3: 3 ziyaret edilmemiş → özyinelemeli çağrı.
scc(3): indeks ve dusuk = 4; 3 yığına itilir.
3 → 4: 4 ziyaret edilmemiş → özyinelemeli çağrı.
scc(4): indeks ve dusuk = 5; 4 yığına itilir.
4 → 3: 3 yığında (geri kenar) → dusuk[4] = min(5, indeks[3] = 4) = 4.
dusuk[4] = 4 ≠ indeks[4] = 5 → 4 kök değil, yığında kalır; çağrı döner.
scc(4) döndü: dusuk[3] = min(4, dusuk[4] = 4) = 4.
dusuk[3] == indeks[3] = 4 → 3 köktür; 4, 3 çekilip yazdırılır: SCC {3, 4}.
scc(3) döndü: dusuk[2] = min(1, dusuk[3] = 4) = 1.
dusuk[2] = 1 ≠ indeks[2] = 3 → 2 kök değil, yığında kalır; çağrı döner.
scc(2) döndü: dusuk[1] = min(2, dusuk[2] = 1) = 1.
dusuk[1] = 1 ≠ indeks[1] = 2 → 1 kök değil, yığında kalır; çağrı döner.
scc(1) döndü: dusuk[0] = min(1, dusuk[1] = 1) = 1.
dusuk[0] == indeks[0] = 1 → 0 köktür; 2, 1, 0 çekilip yazdırılır: SCC {0, 1, 2}.
void scc(int v) { indeks[v] = dusuk[v] = ++sayac; yigin.push(v); yiginda[v] = true; for (int w : komsu[v]) { if (indeks[w] == 0) { scc(w); dusuk[v] = Math.min(dusuk[v], dusuk[w]); } else if (yiginda[w]) dusuk[v] = Math.min(dusuk[v], indeks[w]); } if (dusuk[v] == indeks[v]) { int w; do { w = yigin.pop(); yiginda[w] = false; System.out.print(w + " "); } while (w != v); System.out.println(); } }
Tarjan SCC
Karmaşıklık ve Kosaraju
Tarjan'ın karmaşıklığı: O(V + E)
- V düğüm sayısı, E kenar sayısı.
- Her düğüm bir kez ziyaret edilir, yığına bir kez itilip bir kez çekilir: O(V).
- Her kenar bir kez incelenir: O(E).
- Ek bellek: indeks, enDüşükBağ, yiginda dizileri ve yığın: O(V).
Kosaraju algoritması
- DFS ile düğümleri bitiş sırasına göre yığına it.
- Çizgenin tersini (transpose) oluştur.
- Yığından çekilen sırayla ters çizgede DFS; her DFS ağacı bir SCC.
"Tarjan Adım Adım" çizgesinde Kosaraju da aynı dört SCC'yi bulur: {1, 2, 3}, {4, 5}, {6, 7}, {8}.
| Tarjan | Kosaraju | |
|---|---|---|
| DFS sayısı | 1 | 2 (biri ters çizgede) |
| Ek yapı | indeks / enDüşükBağ, yığın | ters çizge (O(V + E) bellek) |
| Zaman | O(V + E) | O(V + E) |
Uygulamalar
Topolojik Sıralama
Giriş dereceleri: 0:0, 1:1, 2:2, 3:1, 4:1, 5:1, 6:2, 7:1
- Döngüsüz yönlü çizgede (DAG) her u → v kenarı için u'nun v'den önce geldiği sıralama: ön koşullu dersler, derleme sırası, görev planlama.
- Kahn (BFS tabanlı): giriş derecesi 0 olanları kuyruğa koy; çıkarılan düğümün kenarlarını sil, derecesi 0'a düşeni kuyruğa ekle.
- DFS tabanlı: düğümleri bitiş sırasına göre listele, listeyi ters çevir.
| Kahn | 0 1 7 2 3 4 5 6 |
| DFS (ters bitiş) | 0 7 1 2 4 3 5 6 |
Uygulamalar
Gezinmenin Diğer Kullanımları
Özet
Özet
| Konu | Hatırlanacaklar |
|---|---|
| Gösterim | liste O(V + E) bellek, matris O(V²); gezinme listeyle O(V + E), matrisle O(V²) |
| DFS | yığın / özyineleme; derine in, geri dön; ziyaret dizisi döngüye karşı şart |
| Yığınla DFS | çekerken işaretle; bir düğüm birden çok kez itilebilir; sıra komşu sırasının tersi |
| BFS | kuyruk; eklerken işaretle; katman katman; ata dizisiyle ağırlıksız en kısa yol |
| Labirent | hücre = düğüm; BFS tüm yolları eşzamanlı dener, çıkışa ilk ulaştığında yol en kısadır |
| SCC | karşılıklı ulaşılabilir maksimal küme; bileşen çizgesi DAG'dir |
| Tarjan | indeks = keşif sırası; enDüşükBağ; enDüşükBağ = indeks → kök, yığından SCC çek; O(V + E) |
| Uygulamalar | topolojik sıra (Kahn / ters bitiş), bileşenler, iki parçalılık, döngü, köprü |
Özet
Etkileşimli Simülatörler
Özet
Sık Yapılan Hatalar
min(…, indeks(w)) yapılır; SCC'si bulunmuş w yok sayılır. Aksi hâlde kökler kaybolur.Özet
Örnek Problemler
Yönsüz çizge, komşular alfabetik: A–B, A–C, B–D, C–D, C–E, D–F, E–F, F–G.
- A'dan BFS ziyaret sırasını ve her düğümün uzaklığını yazın.
- A'dan özyinelemeli DFS ziyaret sırası nedir?
- A'dan G'ye en kısa yol (BFS ata dizisiyle) nedir?
- Çizge iki parçalı mıdır? Kümeleri verin.
- Yönlü çizge 0→1, 1→2, 2→0, 1→3, 3→4, 4→5, 5→3, 5→6: Tarjan (0'dan, indeks 1'den) ile SCC'leri bulunma sırasıyla yazın.
- A→C, B→C, B→D, C→E, D→E, E→F için Kahn sırası (kuyruk alfabetik başlar)?
Cevapları göster
- A B C D E F G; uzaklıklar A0, B1, C1, D2, E2, F3, G4.
- A → B → D → C → E → F → G: A B D C E F G.
- Ata: G←F, F←D, D←B, B←A → A–B–D–F–G (4 kenar).
- Evet: {A, D, E, G} ve {B, C, F}; tüm döngüler (A-B-D-C, C-D-F-E) çift uzunlukta.
- {6} (6: 7,7), {3, 4, 5} (indeks 4, 5, 6; enDüşükBağ 4), {0, 1, 2} (enDüşükBağ 1).
- Kuyruk [A, B] → A B C D E F.
Özet
Alıştırmalar
- Örnek problemlerdeki çizgede A'dan yığınla DFS (komşular alfabetik itilir) sırası nedir? Toplam kaç itme yapılır?
- V = 1000, E = 3000 olan bir çizgede BFS'in komşuluk listesi ve matris ile yaklaşık iş miktarını karşılaştırın.
- BFS'in ağırlıklı çizgede en kısa yolu bulamadığını gösteren küçük bir örnek verin.
- Bir yönsüz çizgenin bağlı bileşen sayısını bulan algoritmayı tarif edin; karmaşıklığı nedir?
- Tarjan'da "bitmiş SCC'deki w" için
min(v.enDüşükBağ, w.indeks)yapılsaydı "Tarjan Adım Adım" örneğinde ne olurdu?
Cevapları göster
- A C E F G D B; her düğüm bir kez işlenir ve 2E = 16 komşu itilir, başlangıçla 17 itme.
- Liste: V + 2E ≈ 1000 + 6000 = 7000 işlem. Matris: V² = 1 000 000 hücre: ≈ 140 kat fazla.
- s→t (ağırlık 10), s→a (1), a→t (1): BFS t'ye 1 kenarla (maliyet 10) ulaşır; en ucuz yol s→a→t (maliyet 2).
- Sayaç = 0; her v için ziyaret edilmemişse sayaç++ ve v'den DFS/BFS. O(V + E).
- 4 → 2 kenarı enDüşükBağ(4)'ü 2'ye, 6 → 3 kenarı enDüşükBağ(6)'yı 3'e, 8 → 5 kenarı enDüşükBağ(8)'i 5'e indirir: 4, 6 ve 8 kök sayılmaz; {4, 5}, {6, 7}, {8} hiç çıkarılmaz, 4–8 yığında kalır.
Özet
Kaynaklar
- Cormen, Leiserson, Rivest ve Stein, Introduction to Algorithms, 4. baskı, MIT Press, 2022 (Bölüm 20: Elementary Graph Algorithms).
- Sedgewick ve Wayne, Algorithms, 4. baskı, Addison-Wesley, 2011 (Bölüm 4.1–4.2).
- R. E. Tarjan, "Depth-First Search and Linear Graph Algorithms", SIAM Journal on Computing 1(2), 1972.
- A. B. Kahn, "Topological Sorting of Large Networks", Communications of the ACM 5(11), 1962.
- Kleinberg ve Tardos, Algorithm Design, Pearson, 2006 (Bölüm 3: Graphs).