CS/Algorithm
싸이클 그래프 찾기
민트초코 개발자
2025. 4. 12. 15:37
BFS(또는 DFS) 이용한 사이클 검출
기본 원리
- 방문 기록: 각 정점을 탐색하면서 방문 여부를 기록
- 부모 정보 관리: 현재 노드의 인접 노드를 탐색할 때, "바로 이전에 방문한 노드(부모)"는 제외. 무방향 그래프에서는 부모 노드와의 연결은 정상적 연결이기 때문
예제 코드 (BFS)
import java.util.*;
public class CycleDetectionBFS {
public static boolean hasCycle(List<Integer>[] graph, int n) {
boolean[] visited = new boolean[n];
// 모든 연결 요소에 대해 BFS 수행
for (int i = 0; i < n; i++) {
if (!visited[i] && bfs(graph, i, visited)) {
return true;
}
}
return false;
}
private static boolean bfs(List<Integer>[] graph, int start, boolean[] visited) {
Queue<int[]> queue = new LinkedList<>();
// {현재 노드, 부모 노드}; 시작 노드는 부모가 없으므로 -1 사용
queue.add(new int[] { start, -1 });
visited[start] = true;
while (!queue.isEmpty()) {
int[] curInfo = queue.poll();
int cur = curInfo[0];
int parent = curInfo[1];
for (int neighbor : graph[cur]) {
if (neighbor == parent) continue; // 부모 노드는 무시
if (visited[neighbor]) {
// 이미 방문된 노드인데 부모가 아니라면 싸이클
return true;
}
visited[neighbor] = true;
queue.add(new int[] { neighbor, cur });
}
}
return false;
}
}
Union-Find(서로소 집합) 이용한 사이클 검출
기본 원리
- 초기화:
- 각 정점을 자기 자신을 대표하는 집합으로 초기화
- Union 연산:
- 만약 두 정점의 대표가 같다면, 이미 동일 집합에 속해 있으므로 이 간선을 추가하면 싸이클이 발생한 것
- 서로 다른 집합이면 두 집합을 합칩니다. 이때 합치는 순서는 특별한 기준(예: 번호가 작은 쪽을 대표)으로 정할 수 있습니다
- 간선 (u, v) 가 주어질 때, 두 정점의 대표 노드를 찾는다
- 추가적으로 두 집합 중 하나라도 이미 싸이클이 발생한 집합이라면, 합쳐진 집합 역시 싸이클이 있는 집합
예제 코드 (Union-Find)
public class CycleDetectionUnionFind {
int[] parent;
boolean[] hasCycle;
// 생성자: n개의 정점이 있다고 가정
public CycleDetectionUnionFind(int n) {
parent = new int[n];
hasCycle = new boolean[n];
for (int i = 0; i < n; i++) {
parent[i] = i;
hasCycle[i] = false; // 초기에는 싸이클이 없다고 가정
}
}
// 경로 압축을 적용한 find 메서드
public int find(int a) {
if (parent[a] == a) return a;
return parent[a] = find(parent[a]);
}
// union 연산: 두 노드를 합치고 싸이클 여부를 반영
public void union(int a, int b) {
int parentA = find(a);
int parentB = find(b);
// 만약 두 노드가 이미 같은 집합이면, 싸이클 발생!
if (parentA == parentB) {
hasCycle[parentA] = true;
return;
}
// 두 집합 중 하나라도 싸이클이면 합친 집합에도 싸이클
boolean cycleFlag = hasCycle[parentA] || hasCycle[parentB];
// 대표를 번호가 작은쪽으로 결정 (단순한 기준)
if (parentA < parentB) {
parent[parentB] = parentA;
hasCycle[parentA] = cycleFlag; // 싸이클 정보 전파
} else {
parent[parentA] = parentB;
hasCycle[parentB] = cycleFlag;
}
}
}
BFS/DFS vs. Union-Find 비교
BFS / DFS | - 각 정점을 탐색하며 방문 여부 및 부모 정보를 관리 - 연결 요소 내부를 순회하며 싸이클 판별 |
- 그래프의 구조를 명시적으로 탐색할 때 - 문제에서 모든 경로를 탐색해야 하는 경우 |
Union-Find | - 각 정점을 독립 집합으로 시작하여, 간선 추가 시 집합을 합침 - 대표 노드를 통해 싸이클 판별 |
- 간선의 추가/삭제가 빈번한 동적 문제 - 연결 요소의 관리에 집중할 때 |
두 방법 모두 상황에 맞게 사용될 수 있으며, Union-Find는 특히 간선의 개수가 많거나 연결 여부를 빠르게 판별해야 할 때 효율적