naive: R ← {s}, 계속 R 인접 정점 추가 시도. 언제 멈춰야 할지 모름, 무한 루프.
traversal: visited 집합 유지. 각 정점을 정확히 한 번 만 처리. O(V + E).
핵심 통찰: “이미 방문했다” 플래그만으로 완전 탐색이 종료 보장. 방문 순서에 따라 BFS / DFS 등 분화.
시각화
핵심 아이디어
visit / process / finish 패턴
이벤트
시점
의미
visit
정점을 큐/스택에 넣음
”발견” 시각
process
큐/스택에서 꺼냄
인접 정점 검사
finish
모든 인접 정점 처리 완료
(DFS 에서 중요) out-time
invariant: 한 정점은 최대 한 번 process. visited[v] = true 이면 v 는 이미 큐/스택에 들어갔거나 처리 완료.
BFS: 큐 (FIFO), 거리 증가 순. DFS: 스택 또는 재귀 (LIFO), 깊이 우선.
공통 의사코드
traverse(G, s): Q ← init_structure() # BFS: queue, DFS: stack visited ← false[|V|] visited[s] = true Q.add(s) while Q is not empty: u = Q.extract() # FIFO/LIFO 따라 BFS/DFS process(u) for v in G.adj[u]: if not visited[v]: visited[v] = true Q.add(v) parent[v] = u # 경로 복원용
// 그래프 순회 프레임워크, BFS/DFS 선택 가능#include <bits/stdc++.h>using namespace std;vector<int> adj[100005];bool visited[100005];int parent[100005];void bfs(int s) { queue<int> q; visited[s] = true; q.push(s); cout << "BFS: "; while (!q.empty()) { int u = q.front(); q.pop(); cout << u << " "; for (int v : adj[u]) { if (!visited[v]) { visited[v] = true; parent[v] = u; q.push(v); } } } cout << "\n";}void dfs_impl(int u) { visited[u] = true; cout << u << " "; for (int v : adj[u]) { if (!visited[v]) dfs_impl(v); }}void dfs(int s) { cout << "DFS: "; dfs_impl(s); cout << "\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); } // BFS memset(visited, 0, sizeof visited); bfs(s); // DFS memset(visited, 0, sizeof visited); dfs(s);}
# collections.deque + 재귀 DFSfrom collections import dequeimport syssys.setrecursionlimit(200000)input = sys.stdin.readlinen, m, s = map(int, input().split())adj = [[] for _ in range(n + 1)]for _ in range(m): u, v = map(int, input().split()) adj[u].append(v) adj[v].append(u)# BFSvisited = [False] * (n + 1)q = deque([s])visited[s] = Truepath = []while q: u = q.popleft() path.append(u) for v in adj[u]: if not visited[v]: visited[v] = True q.append(v)print("BFS:", *path)# DFSvisited = [False] * (n + 1)dfs_path = []def dfs(u): visited[u] = True dfs_path.append(u) for v in adj[u]: if not visited[v]: dfs(v)dfs(s)print("DFS:", *dfs_path)
// BFS/DFS 통합 구현import java.util.*;import java.io.*;public class Main { static List<Integer>[] adj; static boolean[] visited; static void bfs(int s) { Queue<Integer> q = new ArrayDeque<>(); visited[s] = true; q.add(s); System.out.print("BFS: "); while (!q.isEmpty()) { int u = q.poll(); System.out.print(u + " "); for (int v : adj[u]) { if (!visited[v]) { visited[v] = true; q.add(v); } } } System.out.println(); } static void dfs(int u) { visited[u] = true; System.out.print(u + " "); for (int v : adj[u]) { if (!visited[v]) dfs(v); } } public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); int n = Integer.parseInt(st.nextToken()); int m = Integer.parseInt(st.nextToken()); int s = Integer.parseInt(st.nextToken()); adj = new ArrayList[n + 1]; for (int i = 0; i <= n; i++) adj[i] = new ArrayList<>(); for (int i = 0; i < m; i++) { st = new StringTokenizer(br.readLine()); int u = Integer.parseInt(st.nextToken()); int v = Integer.parseInt(st.nextToken()); adj[u].add(v); adj[v].add(u); } visited = new boolean[n + 1]; bfs(s); visited = new boolean[n + 1]; System.out.print("DFS: "); dfs(s); System.out.println(); }}
stdin
4 5 11 21 31 42 43 4
결과
BFS: 1 2 3 4 DFS: 1 2 4 3
복잡도
항목
값
시간 (인접 리스트)
O(V + E)
시간 (인접 행렬)
O(V²)
공간
O(V) (visited + 큐/스택)
재귀 DFS 추가 공간
O(깊이) (call stack)
변형 / 활용
알고리즘
순회 base
변경점
Dijkstra
BFS
priority queue (거리 순)
A*
BFS
priority queue + heuristic
0/1 BFS
BFS
deque, 0 가중치 edge 먼저
Flood Fill
BFS/DFS
2D grid, 연결 영역 색칠
Bipartite Check
BFS/DFS
색깔 2개 교대로 칠함
위상 정렬
DFS
finish 순서 역순
SCC (Kosaraju)
DFS
두 번 DFS
Articulation Point
DFS
in/out time 비교
함정
1. visited 초기화 잊음
여러 번 순회하려면 visited 를 매번 초기화. 글로벌 배열이면 특히 조심.
2. directed vs undirected
무향 그래프를 입력 받을 때 양방향 간선 두 번 추가 (u→v, v→u). 방향 그래프면 한 번만.
3. 재귀 DFS stack overflow
Python 은 기본 recursion limit 1000. sys.setrecursionlimit(...) 로 늘려야 함. C++ 도 깊이 > 10^5 면 스택 크기 조심.
💬 댓글