PS❯BOJ2021-10-083 min read
[BOJ 2150] Strongly Connected Component - Java (Kosaraju & Tarjan SCC 기초)
방향 그래프에서 상호 도달 가능한 정점들의 집합인 SCC(강한 연결 요소)를 코사라주 또는 타잔 알고리즘을 통해 O(V+E)로 분리하고 정렬 출력하는 기본 풀이
#BOJ#SCC#강한연결요소#Kosaraju#Tarjan#Java
1. 문제 개요 및 조건 분석
| 항목 | 내용 |
|---|---|
| 문제 이름 | Strongly Connected Component (백준 2150번 / 플래티넘 5) |
| 플랫폼 / 난이도 | 백준 (플래티넘 5, 2150번) |
| 문제 링크 | 백준 2150번 |
| 제한 사항 | 시간 제한 2초, 메모리 제한 128MB |
| 입력 조건 | 정점 수 $V \le 10,000$, 간선 수 $E \le 100,000$ (방향 그래프) |
| 핵심 규칙 | 그래프의 모든 **SCC(강한 연결 요소)**를 구하고, 각 컴포넌트 내부 정점 오름차순 및 컴포넌트의 첫 번째 정점 오름차순으로 정렬 출력 |
2. 접근 아이디어: Tarjan SCC 분리 알고리즘
강한 연결 요소 (Strongly Connected Component)
- 유향 그래프에서 두 정점 $u, v$ 사이에 양방향으로 경로가 존재하는 극대 부분 그래프입니다.
- Tarjan 알고리즘:
- DFS 순회 방문 순서(
order[u])를 기록하고 스택에 정점을 푸시합니다. - 인접한 정점들을 탐색하며 서브트리에서 도달 가능한 가장 높은 조상 순서
low를 계산합니다. low == order[u]인 지점이 SCC의 루트이므로, 스택에서 $u$가 나올 때까지 pop하여 하나의 SCC 그룹을 생성합니다.
- DFS 순회 방문 순서(
3. Java 풀이 코드
import java.io.*;
import java.util.*;
public class Main {
static int v, e;
static List<Integer>[] adj;
static int[] order;
static boolean[] finished;
static Stack<Integer> stack = new Stack<>();
static int timer = 1;
static List<List<Integer>> sccList = new ArrayList<>();
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
v = Integer.parseInt(st.nextToken());
e = Integer.parseInt(st.nextToken());
adj = new ArrayList[v + 1];
for (int i = 1; i <= v; i++) adj[i] = new ArrayList<>();
for (int i = 0; i < e; i++) {
st = new StringTokenizer(br.readLine());
int a = Integer.parseInt(st.nextToken());
int b = Integer.parseInt(st.nextToken());
adj[a].add(b);
}
order = new int[v + 1];
finished = new boolean[v + 1];
for (int i = 1; i <= v; i++) {
if (order[i] == 0) dfs(i);
}
// 각 SCC 내부 정점 오름차순 정렬
for (List<Integer> component : sccList) {
Collections.sort(component);
}
// 첫 번째 원소 기준 SCC 그룹 정렬
sccList.sort(Comparator.comparingInt(a -> a.get(0)));
StringBuilder sb = new StringBuilder();
sb.append(sccList.size()).append("\n");
for (List<Integer> comp : sccList) {
for (int node : comp) {
sb.append(node).append(" ");
}
sb.append("-1\n");
}
System.out.print(sb);
}
static int dfs(int u) {
order[u] = timer++;
stack.push(u);
int parent = order[u];
for (int next : adj[u]) {
if (order[next] == 0) {
parent = Math.min(parent, dfs(next));
} else if (!finished[next]) {
parent = Math.min(parent, order[next]);
}
}
if (parent == order[u]) {
List<Integer> component = new ArrayList<>();
while (true) {
int node = stack.pop();
finished[node] = true;
component.add(node);
if (node == u) break;
}
sccList.add(component);
}
return parent;
}
}
4. 복잡도 분석
- 시간 복잡도: $O(V + E)$ — DFS 1회 순회로 모든 SCC 추출 후 정렬 $O(V \log V)$. 0.3초 이내에 매우 안정적으로 완료.
- 공간 복잡도: $O(V + E)$ — 인접 리스트 및 스택.
5. 채점 결과 및 통과 인증

← PREVIOUS LOG
[프로그래머스] 전력망을 둘로 나누기 - Java (트리 간선 절단 & BFS 노드 수 차이 최소화)
NEXT LOG →[프로그래머스] 디스크 컨트롤러 - Java (SJF 스케줄링 & 최소 힙 PriorityQueue)
COMMENTS // GITHUB DISCUSSIONS (GISCUS)