합집합 연산을 확인하여 서로 연결된 두 노드 A, B 를 확인
I. A 와 B의 루트 노드 A’, B’ 를 각각 찾음
II. A’ 를 B’ 의 부모 노드로 설정(B’ 가 A’ 를 가리키도록 함)
모든 합집합 연산을 처리할 때까지 1번 과정을 반복함
**경로 압축 기법**
을 적용하여 최적화한 코드import java.util.Scanner;
public class UnionFind {
// 노드의 개수(V) 와 간선(Union 연산) 의 개수(E)
// 노드의 개수는 최대 100,000개라고 가정
public static int v, e;
public static int[] parent = new int[100001]; // 부모 테이블 초기화
// 특정 원소가 속한 집합 찾기(Find 연산)
public static int findParent(int x) {
// 루트 노드가 아니라면 루트 노드를 찾을 때까지 재귀적으로 호출
if (x == parent[x])
return x;
return parent[x] = findParent(parent[x]);
}
// 두 원소가 속한 집합을 합치기(Union 연산)
public static void unionParent(int a, int b) {
a = findParent(a);
b = findParent(b);
// 둘 중 작은 값을 부모 노드로 설정함
if (a < b)
parent[b] = a;
else
parent[a] = b;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
v = sc.nextInt();
e = sc.nextInt();
// 부모 테이블상에서, 부모를 자기 자신으로 초기화
for (int i = 1; i <= v; i++) {
parent[i] = i;
}
// Union 연산 수행
for (int i = 0; i < e; i++) {
int a = sc.nextInt();
int b = sc.nextInt();
unionParent(a, b);
}
// 각 원소가 속한 집합 출력
System.out.print("각 원소가 속한 집합: ");
for (int i = 1; i <= v; i++) {
System.out.print(findParent(i) + " ");
}
System.out.println();
// 부모 테이블 내용 출력하기
System.out.print("부모 테이블: ");
for (int i = 1; i <= v; i++) {
System.out.print(parent[i] + " ");
}
System.out.println();
sc.close();
}
}
무향 그래프 내에서의 사이클 판별 시에 사용이 가능
방향 그래프에서의 사이클 여부는 DFS(Depth First Search)를 이용하여 판별 가능
간선을 하나씩 확인하면서 두 노드가 포함되어 있는 집합을 합치는(Union) 과정을 반복하는 것으로 판별 가능
과정
각 간선을 확인하며 두 노드의 루트 노드를 확인
I. 루트 노드가 서로 다르다면 두 노드에 대하여 Union 연산 수행
II. 루트 노드가 서로 같다면 사이클이 발생한 것
그래프에 포함되어 있는 모든 간선에 대하여 1번 과정을 반복
구현
import java.util.Scanner;
public class Cycle {
// 노드의 개수(V) 와 간선(Union 연산) 의 개수(E)
// 노드의 개수는 최대 100,000개라고 가정
public static int v, e;
public static int[] parent = new int[100001]; // 부모 테이블 초기화
// 특정 원소가 속한 집합 찾기
public static int findParent(int x) {
// 루트 노드가 아니라면 루트 노드를 찾을 때까지 재귀적으로 호출
if (x == parent[x])
return x;
return parent[x] = findParent(parent[x]);
}
// 두 원소가 속한 집합을 합치기
public static void unionParent(int a, int b) {
a = findParent(a);
b = findParent(b);
// 둘 중 작은 값을 부모 노드로 설정함
if (a < b)
parent[b] = a;
else
parent[a] = b;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
v = sc.nextInt();
e = sc.nextInt();
// 부모 테이블상에서, 부모를 자기 자신으로 초기화
for (int i = 1; i <= v; i++) {
parent[i] = i;
}
boolean cycle = false;
for (int i = 0; i < e; i++) {
int a = sc.nextInt();
int b = sc.nextInt();
// 사이클이 발생한 경우 종료
if (findParent(a) == findParent(b)) {
cycle = true;
break;
}
// 사이클이 발생하지 않았으면 Union 연산 수행
else {
unionParent(a, b);
}
}
if (cycle) {
System.out.println("사이클이 발생했습니다.");
} else {
System.out.println("사이클이 발생하지 않았습니다.");
}
sc.close();
}
}
**가장 거리가 짧은 간선**
부터 집합에 포함시킴신장 트리(Spanning Tree) : 하나의 그래프가 있을 때, 모든 노드를 포함하면서 사이클이 존재하지 않는 부분 그래프
최소 신장 트리 알고리즘(Minimum Spanning Tree Algorithm) : 신장 트리 중에서 최소 비용으로 만들 수 있는 신장 트리를 찾는 알고리즘
간선 데이터를 비용에 따라 오름차순으로 정렬
간선을 하나씩 확인하며 현재의 간선이 사이클을 발생시키는지 확인
I. 사이클이 발생하지 않는 경우 MST 에 포함시킴
II. 사이클이 발생하는 경우 MST 에 포함시키지 않음
모든 간선에 대하여 2의 과정을 반복함
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
class Edge implements Comparable<Edge> {
private int distance;
private int nodeA;
private int nodeB;
public Edge(int distance, int nodeA, int nodeB) {
this.distance = distance;
this.nodeA = nodeA;
this.nodeB = nodeB;
}
public int getDistance() {
return this.distance;
}
public int getNodeA() {
return this.nodeA;
}
public int getNodeB() {
return this.nodeB;
}
// 거리(비용)가 짧은 것이 높은 우선순위를 가지도록 설정
@Override
public int compareTo(Edge other) {
if (this.distance < other.distance) {
return -1;
}
return 1;
}
}
public class Kruskal {
// 노드의 개수(V)와 간선(Union 연산)의 개수(E)
// 노드의 개수는 최대 100,000개라고 가정
public static int v, e;
public static int[] parent = new int[100001]; // 부모 테이블 초기화하기
// 모든 간선을 담을 리스트와, 최종 비용을 담을 변수
public static ArrayList<Edge> edges = new ArrayList<>();
public static int result = 0;
// 특정 원소가 속한 집합 찾기
public static int findParent(int x) {
// 루트 노드가 아니라면 루트 노드를 찾을 때까지 재귀적으로 호출
if (x == parent[x])
return x;
return parent[x] = findParent(parent[x]);
}
// 두 원소가 속한 집합을 합치기
public static void unionParent(int a, int b) {
a = findParent(a);
b = findParent(b);
// 둘 중 작은 값을 부모 노드로 설정함
if (a < b)
parent[b] = a;
else
parent[a] = b;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
v = sc.nextInt();
e = sc.nextInt();
// 부모 테이블상에서, 부모를 자기 자신으로 초기화
for (int i = 1; i <= v; i++) {
parent[i] = i;
}
// 모든 간선에 대한 정보를 입력 받기
for (int i = 0; i < e; i++) {
int a = sc.nextInt();
int b = sc.nextInt();
int cost = sc.nextInt();
edges.add(new Edge(cost, a, b));
}
// 간선을 비용순으로 정렬
Collections.sort(edges);
// 간선을 하나씩 확인하며
for (int i = 0; i < edges.size(); i++) {
int cost = edges.get(i).getDistance();
int a = edges.get(i).getNodeA();
int b = edges.get(i).getNodeB();
// 사이클이 발생하지 않는 경우에만 집합에 포함
if (findParent(a) != findParent(b)) {
unionParent(a, b);
result += cost;
}
}
System.out.println(result);
sc.close();
}
}
순서가 정해져 있는 일련의 작업을 차례대로 수행해야 할 때 사용가능한 알고리즘
방향 그래프의 모든 노드를 **방향성에 거스르지 않도록 순서대로 나열하는 것**
답안이 여러가지가 될 수 있음
과정
진입차수가 0인 노드를 큐에 넣음
큐가 빌 때까지 다음의 과정을 반복함
I. 큐에서 원소를 꺼내 해당 노드에서 출발하는 간선을 그래프에서 제거
II. 새롭게 진입차수가 0이 된 노드를 큐에 넣음
구현
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class TopoolgySort {
// 노드의 개수(V)와 간선의 개수(E)
// 노드의 개수는 최대 100,000개라고 가정
public static int v, e;
// 모든 노드에 대한 진입차수는 0으로 초기화
public static int[] indegree = new int[100001];
// 각 노드에 연결된 간선 정보를 담기 위한 연결 리스트 초기화
public static ArrayList<ArrayList<Integer>> graph = new ArrayList<ArrayList<Integer>>();
// 위상 정렬 메소드
public static void topologySort() {
ArrayList<Integer> result = new ArrayList<>();
Queue<Integer> q = new LinkedList<>();
// 처음 시작할 때는 진입차수가 0인 노드를 큐에 삽입
for (int i = 1; i <= v; i++) {
if (indegree[i] == 0) {
if (indegree[i] == 0) {
q.offer(i);
}
}
}
// 큐가 빌 때까지 반복
while(!q.isEmpty()) {
// 큐에서 원소 꺼내기
int now = q.poll();
result.add(now);
// 해당 원소와 연결된 노드들의 진입차수에서 1 빼기
for (int i = 0; i< graph.get(now).size(); i++) {
indegree[graph.get(now).get(i)]--;
// 새롭게 진입차수가 0이 되는 노드를 큐에 삽입
if (indegree[graph.get(now).get(i)] == 0) {
q.offer(graph.get(now).get(i));
}
}
}
// 위상 정렬을 수행한 결과 출력
for (int i = 0; i < result.size(); i++) {
System.out.print(result.get(i) + " ");
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
v = sc.nextInt();
e = sc.nextInt();
// 그래프 초기화
for (int i = 0; i <= v; i++) {
graph.add(new ArrayList<Integer>());
}
// 방향 그래프의 모든 간선 정보를 입력 받기
for (int i = 0; i < e; i++) {
int a = sc.nextInt();
int b = sc.nextInt();
graph.get(a).add(b); // 정점 A에서 B로 이동 가능
// 진입 차수를 1 증가
indegree[b]++;
}
topologySort();
sc.close();
}
}