Yönlü ve Yönsüz Graflarda Döngü Algılama
Yönsüz graflarda ebeveyn takibiyle, yönlü graflarda ise DFS renk kodlamasıyla (beyaz/gri/siyah üç durumlu ziyaret takibi) döngüleri algılayın.
Yönlü ve Yönsüz Graflarda Döngü Algılama, CoddyKit'te ücretsiz bir Coding Interview Prep dersidir. Bu, 4 dersinin 4. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, Coding Interview Prep öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. Coding Interview Prep kursu toplamda 4 dersten oluşur.
Döngü Tespiti Neden Önemlidir
Bir çizgedeki döngü, aynı düğümde başlayıp biten bir yoldur. Döngü tespiti birçok algoritmada kritik öneme sahiptir: topolojik sıralama döngülü çizgelerde başarısız olur, bağımlılık çözümleme döngüsel bağımlılıkları tespit etmelidir ve OS zamanlamasında kilitlenme tespiti, kaynak tahsisi çizgelerindeki döngülerin bulunmasını gerektirir. Yaklaşım yönsüz ve yönlü çizgeler arasında farklıdır; bunlar temelde farklı algoritmalar gerektirir.
from collections import defaultdict
# Undirected cycle: A-B-C-A (triangle)
undirected = defaultdict(list)
for u, v in [('A','B'),('B','C'),('C','A')]:
undirected[u].append(v)
undirected[v].append(u)
# Directed cycle: A->B->C->A
directed = defaultdict(list)
for u, v in [('A','B'),('B','C'),('C','A')]:
directed[u].append(v) # one direction only
# Key difference:
# Undirected: edge A-B appears as both A->B and B->A
# Must track parent to distinguish cycle from back-edge to parent
print('Undirected and directed cycles need different detection')DFS ile Yönsüz Döngü Tespiti
Yönsüz bir çizgede DFS, zaten ziyaret edilmiş bir düğümü (yalnızca ziyaret edilmiş olması değil) mevcut yol içinde ziyaret ederse bir döngü vardır. Zorluk şudur: her kenar iki yönde de göründüğü için bir alt düğümü ziyaret ettiğimizde, bu düğümün komşu listesinde mevcut düğümümüz (ebeveyn) de bulunur. Ebeveyne geri dönen kenarı yanlışlıkla döngü olarak işaretlememek için her düğümün ebeveynini takip etmeliyiz. Ebeveynimiz olmayan ziyaret edilmiş bir düğümle karşılaşırsak bir döngü bulmuş oluruz.
def has_cycle_undirected(n, edges):
from collections import defaultdict
graph = defaultdict(list)
for u, v in edges:
graph[u].append(v)
graph[v].append(u)
visited = set()
def dfs(node, parent):
visited.add(node)
for nb in graph[node]:
if nb not in visited:
if dfs(nb, node): # recurse with current as parent
return True
elif nb != parent: # visited and not parent = CYCLE
return True
return False
for node in range(n):
if node not in visited:
if dfs(node, -1): # -1 = no parent for root
return True
return False
print(has_cycle_undirected(4, [(0,1),(1,2),(2,3),(3,1)])) # True
print(has_cycle_undirected(3, [(0,1),(1,2)])) # FalseBFS ile Yönsüz Döngü
Yönsüz bir çizgede BFS döngü tespiti de ziyaret edilen her düğümün ebeveynini takip eder. Bir düğümün komşularını işlerken, bir komşu zaten ziyaret edilmişse ve geçerli düğümün ebeveyni değilse bir döngü vardır. Ebeveynleri saklamak için bir sözlük kullanın. O(V + E) karmaşıklığındaki bu yaklaşım, özyineleme sınırı sorununu ortadan kaldırır ve büyük çizgeler için tercih edilen yinelemeli alternatiftir.
from collections import deque, defaultdict
def has_cycle_bfs_undirected(n, edges):
graph = defaultdict(list)
for u, v in edges:
graph[u].append(v)
graph[v].append(u)
visited = set()
for start in range(n):
if start in visited:
continue
visited.add(start)
parent = {start: -1}
queue = deque([start])
while queue:
node = queue.popleft()
for nb in graph[node]:
if nb not in visited:
visited.add(nb)
parent[nb] = node
queue.append(nb)
elif parent[node] != nb: # visited and not parent = CYCLE
return True
return False
print(has_cycle_bfs_undirected(4, [(0,1),(1,2),(2,0)])) # TrueYönlü Döngü: Ebeveyn Takibi Neden Başarısız Olur
Yönlü bir çizgede ebeveyn takibi yeterli değildir. A→C ve B→C durumunu düşünün: C düğümünün iki 'ebeveyni' vardır, ancak döngü yoktur. Doğru yaklaşım üç durumlu renklendirme kullanır: beyaz (ziyaret edilmemiş), gri (geçerli DFS yolunda/yığınında), siyah (tamamen işlenmiş). DFS sırasında bir gri düğümle karşılaşırsak bir döngü vardır; bu, geçerli yoldaki bir ataya geri kenar bulduğumuz anlamına gelir.
# Three-state DFS coloring:
# WHITE (0): not yet visited
# GRAY (1): currently being visited (in DFS stack)
# BLACK (2): fully visited (all descendants processed)
# Why parent fails for directed graphs:
# A -> C (no cycle)
# B -> C (no cycle)
# If we DFS from A, mark C gray
# Then DFS from B finds C is gray -- but this is NOT a cycle!
# C is gray from A's path, not B's path.
# Parent tracking only works when the back-edge goes to the IMMEDIATE parent.
print('Directed graph: use 3-state coloring (white/gray/black)')Üç Durumlu DFS ile Yönlü Döngü Tespiti
0 (beyaz/ziyaret edilmemiş), 1 (gri/yığında), 2 (siyah/tamamlandı) değerlerini içeren bir state[] dizisi kullanın. DFS'yi başlatın; girişte düğümü gri, çıkışta siyah olarak işaretleyin. DFS gri bir düğüme ulaşırsa geri kenar bulunmuştur; yani bir döngü vardır. Siyah bir düğüme ulaşırsa o yol zaten tamamen incelenmiş ve döngü içermediği için onu atlayın.
def has_cycle_directed(n, edges):
from collections import defaultdict
graph = defaultdict(list)
for u, v in edges:
graph[u].append(v)
state = [0] * n # 0=white, 1=gray, 2=black
def dfs(node):
state[node] = 1 # mark gray (in stack)
for nb in graph[node]:
if state[nb] == 1: # gray = back edge = CYCLE
return True
if state[nb] == 0: # white = unvisited
if dfs(nb):
return True
state[node] = 2 # mark black (fully processed)
return False
for node in range(n):
if state[node] == 0:
if dfs(node):
return True
return False
print(has_cycle_directed(4, [(0,1),(1,2),(2,0),(2,3)])) # True (0->1->2->0)
print(has_cycle_directed(3, [(0,1),(1,2)])) # FalseDers Programı: DAG'de Döngü
Ders Programı (LeetCode #207), ön koşullar verildiğinde tüm derslerin tamamlanıp tamamlanamayacağını sorar. Dersleri düğümler, ön koşulları ise yönlü kenarlar olarak modelleyin. Tüm dersler ancak ve ancak çizge bir DAG ise (döngü yoksa) tamamlanabilir. Üç durumlu DFS döngü tespitini kullanın; bir döngü bulunursa Yanlış döndürün, aksi hâlde Doğru döndürün.
from collections import defaultdict
def can_finish(num_courses, prerequisites):
graph = defaultdict(list)
for a, b in prerequisites:
graph[b].append(a) # b is prerequisite for a: b -> a
state = [0] * num_courses
def dfs(course):
if state[course] == 1: return False # cycle!
if state[course] == 2: return True # already verified
state[course] = 1 # mark as in-progress
for next_course in graph[course]:
if not dfs(next_course):
return False
state[course] = 2 # mark as done
return True
return all(dfs(i) for i in range(num_courses) if state[i] == 0)
print(can_finish(2, [[1,0]])) # True: take 0 then 1
print(can_finish(2, [[1,0],[0,1]])) # False: circular dependencyKahn Algoritmasıyla Döngü Tespiti (BFS)
Yönlü çizgelerde alternatif bir döngü tespiti yaklaşımı, Kahn'ın BFS topolojik sıralamasını kullanır. Tüm düğümlerin giriş derecelerini sayın. Giriş derecesi 0 olan düğümleri bir kuyruğa koyun. Her birini işleyin: komşuların giriş derecelerini azaltın ve 0'a ulaşanları kuyruğa ekleyin. İşlenen düğüm sayısı V'ye eşitse döngü yoktur; aksi hâlde bir döngü vardır (işlenmemiş düğümler döngüler oluşturur). O(V + E) karmaşıklığındaki bu yaklaşım sezgiseldir ve üç durumlu DFS'den daha kolay hatırlanır.
from collections import defaultdict, deque
def has_cycle_kahn(n, edges):
graph = defaultdict(list)
in_degree = [0] * n
for u, v in edges:
graph[u].append(v)
in_degree[v] += 1
# Start with all zero in-degree nodes
queue = deque(i for i in range(n) if in_degree[i] == 0)
processed = 0
while queue:
node = queue.popleft()
processed += 1
for nb in graph[node]:
in_degree[nb] -= 1
if in_degree[nb] == 0:
queue.append(nb)
return processed != n # if not all processed, cycle exists
print(has_cycle_kahn(4, [(0,1),(1,2),(2,0),(2,3)])) # True
print(has_cycle_kahn(3, [(0,1),(1,2)])) # FalseDöngüyü Bulma: Döngü Düğümlerini Toplama
Bazen yalnızca döngünün varlığını tespit etmek değil, hangi düğümlerin döngünün parçası olduğunu belirlemek gerekir. Üç durumlu DFS sırasında bir geri kenar bulunduğunda, ata ile geçerli düğüm arasındaki tüm düğümleri toplamak için çağrı yığını (veya yol yığını) üzerinden geriye doğru ilerleyin. Durum dizisinin yanında tutulan bir yol yığını, geçerli DFS yolunu yakalayarak O(döngü_uzunluğu) karmaşıklığında döngünün yeniden oluşturulmasını sağlar.
def find_cycle_nodes(n, edges):
from collections import defaultdict
graph = defaultdict(list)
for u, v in edges:
graph[u].append(v)
state = [0] * n
path = [] # current DFS path
cycle = []
def dfs(node):
state[node] = 1
path.append(node)
for nb in graph[node]:
if state[nb] == 1: # back edge -> found cycle
start = path.index(nb)
cycle.extend(path[start:])
return True
if state[nb] == 0 and dfs(nb):
return True
path.pop()
state[node] = 2
return False
for i in range(n):
if state[i] == 0 and dfs(i):
break
return cycle
print(find_cycle_nodes(4, [(0,1),(1,2),(2,0),(2,3)])) # [0, 1, 2]Sonunda Güvenli Durumlara Ulaşan Düğümleri Bulma
Sonunda Güvenli Durumlara Ulaşan Düğümleri Bulma (LeetCode #802), döngüde takılmadan sonunda bir son düğüme (çıkış kenarı olmayan düğüme) ulaşan düğümlerin hangileri olduğunu sorar. Bir düğümden başlayan tüm yollar son düğümlere ulaşıyorsa bu düğüm 'güvenli'dir. Üç durumlu DFS kullanın: siyah olan (döngü tespiti yapılmadan tamamen işlenen) düğümler güvenlidir. Bir döngünün parçası olan veya döngüye götüren düğümler güvenli değildir.
def eventual_safe_nodes(graph):
n = len(graph)
state = [0] * n # 0=unvisited, 1=visiting, 2=safe
def dfs(node):
if state[node] == 1: # currently visiting = cycle
return False
if state[node] == 2: # already verified safe
return True
state[node] = 1 # mark as visiting
for nb in graph[node]:
if not dfs(nb):
return False # leads to cycle, not safe
state[node] = 2 # mark as safe
return True
return [i for i in range(n) if dfs(i)]
# [[1,2],[2,3],[5],[0],[5],[],[]] means:
# 0->[1,2], 1->[2,3], 2->[5], 3->[0] (cycle!), 4->[5], 5->[], 6->[]
print(eventual_safe_nodes([[1,2],[2,3],[5],[0],[5],[],[]]))
# [2, 4, 5, 6]Yönsüz Çizgede Gereksiz Bağlantı
Gereksiz Bağlantı (LeetCode #684), döngü içermeyen bir yönsüz çizgeye eklendiğinde döngü oluşturan kenarı bulur. Bu, DFS döngü tespitiyle çözülebilse de en temiz çözüm Birleştirme-Bulma (DSU) kullanır: kenarları birer birer işleyin; her iki uç zaten birbirine bağlıysa (aynı bileşendeyse) geçerli kenar bir döngü oluşturur ve cevap odur. DSU, işlem başına O(alpha(n)) maliyet sunar; bu pratikte O(1)'dir.
def find_redundant_connection(edges):
n = len(edges)
parent = list(range(n + 1))
rank = [0] * (n + 1)
def find(x):
if parent[x] != x:
parent[x] = find(parent[x]) # path compression
return parent[x]
def union(x, y):
px, py = find(x), find(y)
if px == py:
return False # already connected = cycle!
if rank[px] < rank[py]: px, py = py, px
parent[py] = px
if rank[px] == rank[py]: rank[px] += 1
return True
for u, v in edges:
if not union(u, v):
return [u, v] # this edge creates the cycle
return []
print(find_redundant_connection([[1,2],[1,3],[2,3]])) # [2,3]
print(find_redundant_connection([[1,2],[2,3],[3,4],[1,4],[1,5]])) # [1,4]Özet: Döngü Tespit Stratejileri
Döngü tespit araçlarını özetlemek gerekirse: yönsüz çizgelerde ebeveyn takibiyle DFS veya Birleştirme-Bulma kullanın. Yönlü çizgelerde üç durumlu DFS'yi (beyaz/gri/siyah) veya Kahn'ın BFS topolojik sıralamasını kullanın. Kenarları birer birer (çevrimiçi olarak) ekliyorsanız Birleştirme-Bulma'yı seçin. Topolojik sıraya da ihtiyacınız varsa Kahn algoritmasını seçin. Belirli döngü düğümlerini tanımlamanız gerekiyorsa üç durumlu DFS'yi seçin. Mülakatlarda döngü tespitini tartışırken yönlü ve yönsüz ayrımını her zaman belirtin.
# Cycle detection summary:
# Graph type | Algorithm | Complexity
# ------------|----------------------|-----------
# Undirected | DFS + parent track | O(V + E)
# Undirected | Union-Find (DSU) | O(E * alpha(V))
# Directed | DFS 3-state (W/G/B) | O(V + E)
# Directed | Kahn's BFS topo sort | O(V + E)
# When to choose:
# Online (edges added one at a time): Union-Find
# Need topological order too: Kahn's BFS
# Need cycle nodes identified: 3-state DFS with path stack
# Simple existence check: any of the above
print('Always clarify directed vs undirected before coding')Hızlı Kontrol
Bu dersteki Veri Yapıları & Algoritmalar — Kodlama Mülakatı Hazırlığı kavramlarını anlayıp anlamadığınızı sınayın.
Ders Özeti
Bu derste şunları öğrendiniz: ebeveyn takibi yapan DFS ile yönsüz döngü tespiti, üç durumlu beyaz/gri/siyah renklendirmeyle yönlü döngü tespiti, yönlü çizgeler için Kahn'ın BFS alternatifi ve ders programı, gereksiz bağlantı ve sonunda güvenli durumlara ulaşan düğümler gibi uygulamalar. Sırada dinamik programlamanın temellerine derinlemesine ineceğiz.
Sıkça Sorulan Sorular
“Yönlü ve Yönsüz Graflarda Döngü Algılama” dersi ücretsiz mi?
Evet — “Yönlü ve Yönsüz Graflarda Döngü Algılama” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve Coding Interview Prep kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. Coding Interview Prep kursu toplamda 4 dersten oluşur.
“Yönlü ve Yönsüz Graflarda Döngü Algılama” dersinde ne öğreneceğim?
Yönsüz graflarda ebeveyn takibiyle, yönlü graflarda ise DFS renk kodlamasıyla (beyaz/gri/siyah üç durumlu ziyaret takibi) döngüleri algılayın. Coding Interview Prep ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.
Coding Interview Prep öğrenmeye başlamak için deneyim gerekli mi?
Önceden deneyim gerekmez. CoddyKit'te Coding Interview Prep, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 4. dersidir.
“Yönlü ve Yönsüz Graflarda Döngü Algılama” dersi ne kadar sürer?
Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.
Bu Coding Interview Prep dersinde kod yazıp çalıştırabilir miyim?
Evet. Her Coding Interview Prep dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.
Bu kursun tüm dersleri
- Graf Gösterimleri ve Dolaşım Kurulumu
- BFS: En Kısa Yol ve Seviye Dolaşımı
- DFS: Bağlı Bileşenler ve Alan Doldurma
- Yönlü ve Yönsüz Graflarda Döngü Algılama