본문으로 건너뛰기
김신건의 로그

깊이 우선 탐색 (DFS)

· 수정 · 📖 약 4분 · 1,080자/단어 #algorithm #graph #dfs
DFS (Depth-First Search), DFS, depth-first search, 깊이 우선 탐색, depth-first

정의

깊이 우선 탐색 (Depth-First Search, DFS) 는 그래프 G=(V, E) 에서 갈 수 있는 만큼 깊이 들어가다가 막히면 백트래킹하는 알고리즘. 스택 (LIFO) 또는 재귀 로 구현.

문제 풀이에서 사이클 탐지, 위상 정렬, SCC, articulation point 등의 기초 블록.

문제 상황과 동기

“정점 s 에서 도달 가능한 모든 정점을 방문하되, 방문 시각 (in-time) 과 완료 시각 (out-time) 을 기록하라.”

  • BFS: 최단 거리는 구하지만 in/out-time 개념 없음.
  • DFS: 각 정점의 발견 시각종료 시각 을 기록. 이 시각 정보로 back edge, cross edge, tree edge 판별 → 사이클 / 위상 정렬.

핵심 통찰: 스택 LIFO 순서 = 재귀 호출 순서 = 백트래킹 자동.

시각화

핵심 아이디어

in-time / out-time 패턴

time = 0

DFS_VISIT(u):
    time++
    in[u] = time
    visited[u] = true
    
    for v in adj[u]:
        if not visited[v]:
            parent[v] = u
            DFS_VISIT(v)
    
    time++
    out[u] = time

invariant:

  • 정점 u 의 subtree 는 [in[u], out[u]] 구간에 포함.
  • v 가 u 의 자손in[u] < in[v] < out[v] < out[u].
  • back edge (v → ancestor) 존재 ⇔ 사이클 존재.

알고리즘

DFS(G):
    visited ← false[|V|]
    time ← 0
    for u in V:
        if not visited[u]:
            DFS_VISIT(u)

DFS_VISIT(u):
    time++
    in[u] = time
    visited[u] = true
    for v in G.adj[u]:
        if not visited[v]:
            parent[v] = u
            DFS_VISIT(v)
        else if v is not parent[u] and in[v] < in[u]:
            # back edge, 사이클 존재
    time++
    out[u] = time

구현

// DFS 재귀 구현, in/out time 기록
#include <bits/stdc++.h>
using namespace std;

vector<int> adj[100005];
bool visited[100005];
int in_time[100005], out_time[100005];
int timer = 0;

void dfs(int u) {
  visited[u] = true;
  in_time[u] = ++timer;
  cout << "visit " << u << " (in=" << in_time[u] << ")\n";
  
  for (int v : adj[u]) {
      if (!visited[v])
          dfs(v);
  }
  
  out_time[u] = ++timer;
  cout << "finish " << u << " (out=" << out_time[u] << ")\n";
}

int main() {
  int n, m, s;
  cin >> n >> m >> s;
  for (int i = 0; i < m; i++) {
      int u, v;
      cin >> u >> v;
      adj[u].push_back(v);
      adj[v].push_back(u);
  }
  dfs(s);
}
stdin
4 3 1
1 2
2 3
3 4
결과
visit 1 (in=1)
visit 2 (in=2)
visit 3 (in=3)
visit 4 (in=4)
finish 4 (out=5)
finish 3 (out=6)
finish 2 (out=7)
finish 1 (out=8)

복잡도

항목
시간 (인접 리스트)O(V + E)
시간 (인접 행렬)O(V²)
공간 (재귀)O(V) + O(깊이) (call stack)
공간 (스택 구현)O(V) (명시적 스택)

응용

응용사용 방식
사이클 탐지back edge (v → ancestor) 발견
위상 정렬finish 순서 역순 (out-time 큰 순)
SCC (Kosaraju)DFS 두 번: 1) finish 순서, 2) 역그래프 DFS
Articulation Pointin/low 비교
Bridgelow[v] > in[u]
Bipartite Check색깔 교대 (BFS 와 동일)

함정

1. 재귀 깊이 초과

Python: sys.setrecursionlimit(...) 필수. C++: 스택 크기 제한 조심.

2. visited 초기화

여러 번 DFS 하려면 visited 매번 초기화. 글로벌 배열이면 특히 조심.

3. back edge vs cross edge

무향 그래프에서 모든 non-tree edge 는 back edge. 유향 그래프는 cross edge, forward edge 도 존재.

4. in/out time 기록 시점

in[u] 는 함수 진입 시각, out[u] 는 함수 종료 시각. 두 시각 모두 필요한 알고리즘 (SCC, articulation point) 에서 누락 주의.

5. parent 배열 미관리

무향 그래프에서 u → v → u 같은 양방향 간선을 back edge 로 오인. parent[v] = u 기록 후 v == parent[u] 체크 필요.

BFS vs DFS 비교

특징BFSDFS
자료구조큐 (FIFO)스택 / 재귀 (LIFO)
방문 순서거리 가까운 먼저깊이 우선
최단 경로✓ (무가중)
사이클 탐지✓ (back edge)
위상 정렬✓ (finish 순)
메모리O(너비)O(깊이)

BOJ 연습 문제

번호제목정답률링크
BOJ 1260DFS와 BFS39.1%kokoa-lab
BOJ 2606바이러스 (DFS)54.2%kokoa-lab
BOJ 2667단지번호붙이기43.2%kokoa-lab
BOJ 11724연결 요소의 개수45.8%kokoa-lab
BOJ 1012유기농 배추42.1%kokoa-lab

참고

이 글의 용어 (4개)
강한 연결 요소 (SCC)algorithm
정의 강한 연결 요소 (Strongly Connected Component, SCC) 는 방향 그래프에서 서로 도달 가능한 정점들의 최대 부분 집합. SCC 내 임의 두 정점 u…
그래프 순회 (Graph Traversal)algorithm
정의 그래프 순회 (Graph Traversal) 는 그래프 G=(V, E) 의 모든 정점 V 를 체계적으로 방문하는 알고리즘 패밀리. 대표 구현: 너비 우선 탐색 (BFS), …
너비 우선 탐색 (BFS)algorithm
정의 너비 우선 탐색 (Breadth-First Search, BFS) 는 그래프 G=(V, E) 에서 시작 정점 s 로부터 가까운 정점부터 순서대로 방문하는 알고리즘. 큐 (F…
위상 정렬 (Topological Sorting)algorithm
정의 위상 정렬 (Topological Sorting) 은 방향 비순환 그래프 (DAG) 의 모든 정점을 간선 방향을 어기지 않도록 일렬로 나열하는 것. 간선 가 있으면 정렬 결…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기