출시·고도화 중
그래프 탐색 안내서 · 6/6
그래프 탐색은 코딩 테스트만의 기술이 아닙니다. 빌드 도구, 패키지 관리자, 가비지 컬렉터, 웹 크롤러, 그림 편집기의 채우기 도구가 모두 이 장에서 본 BFS, DFS, 위상 정렬 위에서 동작합니다. 이 장에서는 실제 시스템의 예와 표준 라이브러리, 그리고 자주 하는 실수를 정리합니다.
Python 3.9부터는 표준 라이브러리 graphlib에 위상 정렬이 들어 있습니다. 사전의 값은 "먼저 끝나야 하는 것"(선행 노드)입니다.
from graphlib import TopologicalSorter, CycleError
deps = {"app": {"ui", "net"}, "ui": {"core"}, "net": {"core"}}
print(list(TopologicalSorter(deps).static_order()))
# 예: ['core', 'ui', 'net', 'app'] (같은 단계 안의 순서는 정해져 있지 않음)
try:
list(TopologicalSorter({"a": {"b"}, "b": {"a"}}).static_order())
except CycleError as error:
print("순환 의존:", error.args[1])TopologicalSorter는 prepare(), get_ready(), done()으로 준비된 작업을 여러 작업자에게 나눠 주는 병렬 실행도 지원합니다. 그래프를 본격적으로 다룬다면 NetworkX가 BFS, DFS, 연결 요소, 위상 정렬을 모두 제공합니다.
import networkx as nx
grid = nx.grid_2d_graph(3, 3) # 3 x 3 격자 그래프
print(nx.shortest_path_length(grid, (0, 0), (2, 2))) # 4, 가중치가 없으면 BFS
print(nx.number_connected_components(nx.Graph([(0, 1), (2, 3)]))) # 2
print(list(nx.topological_sort(nx.DiGraph([("core", "ui"), ("ui", "app")]))))가비지 컬렉터의 표시 단계는 결국 반복형 DFS입니다.
def mark(roots, refs):
live, stack = set(), list(roots)
while stack:
obj = stack.pop()
if obj in live:
continue
live.add(obj)
stack.extend(refs.get(obj, []))
return live
refs = {"main": ["a"], "a": ["b"], "b": ["a"], "c": ["d"]}
print(mark(["main"], refs)) # {'main', 'a', 'b'}: c와 d는 회수 대상a와 b가 서로를 참조하는 순환이 있어도 방문 집합 덕분에 끝나고, 루트에서 닿지 않는 c와 d는 참조 횟수와 상관없이 회수됩니다.
list.pop(0)과 JavaScript의 shift()는 O(n)입니다.-1인 정점을 그대로 계산에 쓰지 않도록 확인합니다.의존 관계가 있는 곳에는 위상 정렬이, 도달 가능성을 묻는 곳에는 BFS나 DFS가 있습니다. 실무에서는 graphlib이나 NetworkX 같은 검증된 구현을 먼저 쓰고, 직접 구현할 때는 방문 표시 시점, 큐 자료 구조, 재귀 깊이, 사이클 판단 기준을 확인하세요.
댓글 0개
로그인 · 로그인하면 댓글을 남길 수 있습니다.
첫 댓글을 남겨 보세요.