Derinlik Öncelikli Arama (DFS)
Bir dalı sonuna kadar izleyip geri dönen (backtracking) graf gezme algoritması; döngü bulma ve topolojik sıralamanın temeli.
Önce bunlara göz at
#Nasıl çalışır?
Derinlik öncelikli arama (Depth-First Search, DFS), bir graftaki bir dalı gidebildiği kadar derine izler; çıkmaza girince en son ayrıldığı noktaya geri döner (backtracking) ve oradan başka bir dalı dener.
Bu davranışı bir yığın (stack) sağlar: genellikle özyineleme ile yazılır, bu durumda yığın çağrı yığınının kendisidir. Herhangi bir anda yığındaki düğümler, başlangıçtan şu anki düğüme giden yolu oluşturur.
DFS iki düğüm arasında bir yol bulur ama bu yolun en kısa olacağını garanti etmez. Buna karşılık döngü bulma, topolojik sıralama ve bağlı bileşenler gibi yapısal sorular için çok uygundur.
#Kendin dene
Komşuluk listesi
- 1: 2, 3
- 2: 1, 4, 5
- 3: 1, 5, 6
- 4: 2, 7
- 5: 2, 3, 7
- 6: 3, 7
- 7: 4, 5, 6
Bir başlangıç düğümü seç ve DFS'in bir dalda sonuna kadar inip geri dönüşünü izle. İki düğüm vererek DFS ile bir yol bulabilirsin.
#Adım adım
- 01Başlangıç düğümünü ziyaret et ve yığına koy.
- 02Ziyaret edilmemiş ilk komşuya geç ve aynı işlemi orada tekrarla.
- 03Ziyaret edilmemiş komşu kalmadıysa düğümü yığından çıkar ve bir öncekine geri dön (backtrack).
- 04Yol bulmak için: hedefe ulaşıldığında yığın, başlangıçtan hedefe giden yoldur.
#Örnek kod
Aynı algoritma 8 dilde. Her program derlenip çalıştırılarak test edildi ve hepsi aynı çıktıyı veriyor.
# Undirected graph as adjacency lists (neighbours in increasing order).
EDGES = [(1, 2), (1, 3), (2, 4), (2, 5), (3, 5), (3, 6), (4, 7), (5, 7), (6, 7)]
graph = {v: [] for v in range(1, 8)}
for a, b in EDGES:
graph[a].append(b)
graph[b].append(a)
for v in graph:
graph[v].sort()
def dfs(u: int, visited: set, order: list) -> list:
visited.add(u)
order.append(u)
for v in graph[u]:
if v not in visited:
dfs(v, visited, order) # go deep first; returning = backtracking
return order
def find_path(u: int, target: int, visited: set, path: list) -> bool:
visited.add(u)
path.append(u)
if u == target:
return True
for v in graph[u]:
if v not in visited and find_path(v, target, visited, path):
return True
path.pop() # dead end
return False
print(" ".join(map(str, dfs(1, set(), []))))
path = []
find_path(1, 6, set(), path)
print(" ".join(map(str, path))) # a path, but not the shortest (1 3 6)#Karmaşıklık
| İşlem | Süre |
|---|---|
| Zaman (komşuluk listesi) | O(V + E) |
| Zaman (komşuluk matrisi) | O(V²) |
| Bellek (yığın + ziyaret) | O(V) |
| Yol bulma | O(V + E), en kısa değil |
Her düğüm bir kez ziyaret edilir ve her kenara iki uçtan birer kez bakılır. Özyinelemeli DFS çok derin graflarda (ör. 1 milyon düğümlük bir zincir) çağrı yığınını taşırabilir; o durumda açık bir yığınla iteratif yazmak gerekir.
#Ne zaman kullanılır?
Labirent üretme ve çözme, bulmaca/backtracking problemleri (Sudoku, N-vezir), döngü tespiti, topolojik sıralama (derleme ve görev bağımlılıkları), bağlı ve güçlü bağlı bileşenler, köprü ve kesme noktaları DFS ile çözülür.
#Alıştırma
Soruları cevaplamak ve ilerlemeni kaydetmek için giriş yap.
Kolay
DFS hangi veri yapısıyla çalışır?
- 1Yığın (stack), çoğu zaman çağrı yığını
- 2Kuyruk (queue)
- 3Öncelik kuyruğu
- 4Hash tablosu
DFS'te backtracking ne zaman olur?
- 1Düğümün ziyaret edilmemiş komşusu kalmadığında
- 2Her kenardan sonra
- 3Kuyruk boşaldığında
- 4Hedefe ulaşıldığında
DFS iki düğüm arasında en kısa yolu bulur mu?
- 1Hayır, bulduğu yol daha uzun olabilir
- 2Evet, her zaman
- 3Yalnızca ağırlıklı graflarda
- 4Yalnızca yönlü graflarda
Orta
Hedefe ulaşıldığı anda DFS yığınında ne vardır?
- 1Başlangıçtan hedefe giden yol
- 2Ziyaret edilen tüm düğümler
- 3Hedefin komşuları
- 4Henüz ziyaret edilmemiş düğümler
Zor
Aşağıdaki problemlerden hangisi genellikle DFS ile çözülür?
- 1Topolojik sıralama
- 2Ağırlıksız grafta en kısa yol
- 3Ağırlıklı grafta en kısa yol
- 4Sosyal ağda en yakın arkadaşları katman katman bulmak