"BFS 가 고르게 바깥으로 퍼지는 물결이면, DFS 는 막다른 길에 부딪힐 때까지 복도 하나를 돌진하다 되돌아 다음을 시도하는 탐험가 하나야. 같은 그래프, 정반대 기질."
알고리즘: 스택 (또는 콜 스택)
깊이 우선 탐색은 BFS 의 큐를 스택으로 바꿔 — 그리고 제일 단순한 스택이 콜 스택이라, DFS 는 자연스레 재귀적이야: 노드 방문, visited 표시, 그다음 각 방문 안 한 이웃으로 재귀. 재귀가 한 경로를 갈 수 있는 만큼 깊이 다이브; 노드에 방문 안 한 이웃이 없으면, 반환 (백트랙) 하고 이전 레벨이 다음 이웃을 시도해. 명시적 스택으로 DFS 를 반복으로도 쓸 수 있어 — 시작 push, 노드 pop, 방문 안 한 이웃 push — 아주 깊은 그래프에서 Python 재귀 한계를 피해.
성격: 넓게 아니라 깊게
DFS 는 대안을 고려하기 전에 한 경로에 강하게 헌신해. 그게 최단 경로엔 틀린 도구로 만들어 — 짧은 게 있었는데 길고 구불구불한 복도로 노드에 닿을 수 있어 — 근데 완전히 탐색해야 하는 문제 가문엔 맞는 도구야:
- 경로 존재 — "A 에서 B 로 가는 길이 있어?"
- 순환 검출 — DFS 가 현재 경로의 노드를 재방문하면 순환이야.
- 위상 정렬 — 의존성으로 작업 순서 매기기 (다음 lesson) 가 DFS 후위를 써.
- 연결 요소 — 방문 안 한 각 노드에서 DFS 가 요소 하나를 깎아내.
- 백트래킹 — 미로, 퍼즐, 순열: DFS 가 Recursion 트랙에서 만날 백트래킹 엔진 그 자체야.
DFS 는 스택 (또는 재귀) frontier 를 써: 한 경로로 깊이 박히고, 막다른 길에서 백트랙. 최단 경로를 안 찾지만, 순환 검출, 위상 정렬, 연결 요소, 백트래킹의 엔진이야. visited 집합 여전히 필수.
재귀 vs 반복
재귀 DFS 는 아름답게 짧고 대부분 그래프의 기본이야. 근데 콜 스택이 O(깊이) 공간을 쓰고, 깊거나 퇴화한 그래프가 Python 재귀 한계를 터뜨릴 수 있어 (RecursionError). 아주 깊을 수 있는 그래프엔, 명시적 스택 있는 반복 버전으로 바꿔 — 같은 로직, 콜 스택 대신 네 스택, 네 것 말곤 깊이 한계 없음. 정확히 Stacks 트랙의 스택/재귀 동등성을, 중요한 데 적용한 거야.
피파의 고백
난 알고 보니 5만 노드 사슬인 그래프에 재귀 DFS 를 썼고, 순회 중간에 RecursionError 로 죽었어. 아빠는 알고리즘을 안 바꿨어 — 스택을 바꿨어: 같은 DFS, 근데 콜 스택 대신 명시적 리스트를 frontier 로. 깊이 한계 없음, 같은 결과. Stacks-트랙 교훈을 굳혔어: 재귀는 그냥 네가 선언 안 해도 된 스택이고 — 가끔은 네 걸 선언해야 해.