서로소 집합
서로 중복된 원소가 없는 집합을 의미 == 교집합이 없는 집합
집합에 속한 특정 멤버(대표자)를 통해 집합을 구분
대표자는 어떤 데이터가 되든 상관이 없음
하나의 집합에는 오로지 하나의 대표자만 가질 수 있음
서로소 집합 연산
1) init : 초기화
2) union : 두 개의 집합을 하나의 집합으로 결합
3) find : 집합 내의 대표자를 찾음
서로소의 최적화
- Rank 사용
기본 서로소 함수에서는 부모를 찾을 때 find의 재귀가 여러 번 발생함
-> 두 서로소를 결합할 때 더 낮은 트리를 더 높은 트리의 자식으로 결합하면 효율성을 높일 수 있음
(데이터에 따라서 사용하지 않을 때 더 빠른 경우가 있음)
- Path 사용
find함수의 재귀가 발생하여 부모의 결과를 찾으면, 해당 결과를 변수에 저장함으로써 부모를 찾기 위한 재귀가 적게 일어나도록 유도
서로소는 일반적으로 배열과 리스트를 사용하여 만들 수 있으며,
배열을 통해 구현하는 경우 배열의 인덱스가 노드의 위치를, 배열의 데이터가 연결된 부모 노드를 나타냄
public class Main {
static int N = 8;
static int[] rank = new int[N]; // Rank(높이값) 저장 배열
static int[] parents;
public static void main(String[] args) {
parents = new int[N]; // 각 노드별 대표자 노드 연결 번호를 저장
// 1) make set
for (int i = 0; i < parents.length; i++) {
parents[i] = i;
}
// 2) union
union(0, 2); union(1, 2); union(3, 4); union(6, 4);
union(7, 4); union(4, 5); union(3, 1);
}
private static void union(int a, int b) {
// 3) find
int pa = find(a);
int pb = find(b);
// 두 조직의 대표가 다르면 다른 조직이므로 합침
if (pa != pb) {
// 일반 코드
// parents[pa] = pb;
// rank code
// b조직이 a조직보다 높이가 높다면 a조직이 b조직 밑으로 들어간다.
if (rank[pb] > rank[pa]) {
parents[pa] = parents[pb];
} else {
parents[b] = parents[a];
if (rank[a] == rank[b]) {
rank[b]++;
}
}
}
}
private static int find(int a) {
if (parents[a] == a) {
return a;
} else {
// 일반적인 방식
// return find(parents[a]); // 재귀를 통해 대표자 찾기
// path compression
return parents[a] = find(parents[a]); // 재귀를 통해 대표자 찾기
}
}
}
// 백준(1717) 집합의 표현
public class 집합의표현 {
static int[] parents;
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
StringBuilder sb = new StringBuilder();
ArrayList<ArrayList<Integer>> node = new ArrayList<>();
// 첫 번째 줄
int n = Integer.parseInt(st.nextToken()); // 최대 값
int m = Integer.parseInt(st.nextToken()); // 연산 횟수
// 부모 값 저장
parents = new int[n + 1];
// 초기값 지정
for (int i = 0; i < parents.length; i++) {
parents[i] = i;
}
// 나머지 줄
for (int i = 0; i < m; i++) {
st = new StringTokenizer(br.readLine());
int action = Integer.parseInt(st.nextToken()); // 0 = 합치기, 1 = 합쳐졌는지 확인
int a = Integer.parseInt(st.nextToken());
int b = Integer.parseInt(st.nextToken());
if (action == 0) {
union(a, b);
} else {
int pa = find(a);
int pb = find(b);
// 부모가 같으면
if (pa == pb) {
sb.append("YES\n");
} else {
sb.append("NO\n");
}
}
}
System.out.println(sb);
}
// 합집합
private static void union(int a, int b) {
int pa = find(a);
int pb = find(b);
// 왼쪽 집합을 오른쪽에 합침
parents[pa] = pb;
}
// 부모 찾기
private static int find(int a) {
if (parents[a] == a) {
return a;
} else {
return parents[a] = find(parents[a]);
}
}
}
- 정올 종교 (기본 개념 + 무리 개수 찾기)
http://www.jungol.co.kr/bbs/board.php?bo_table=pbank&wr_id=1136&sca=99&sfl=wr_hit&stx=1863
- SWEA 창용마을무리의개수 (기본 개념 + 무리 개수 찾기)
https://angelplayer.tistory.com/427
- SWEA 서로소집합 (기본 개념)
https://angelplayer.tistory.com/428
https://swexpertacademy.com/main/code/problem/problemDetail.do?contestProbId=AWBJKA6qr2oDFAWr
- 백준 공항 (응용)
https://angelplayer.tistory.com/430
https://www.acmicpc.net/problem/10775
[알고리즘] 이분(이진)탐색 (0) | 2023.03.11 |
---|---|
[알고리즘] 최소 신장 트리 (크루스칼, 프림 알고리즘) (0) | 2023.03.01 |
[알고리즘] DFS (0) | 2023.02.24 |
[알고리즘] BFS (너비 우선 탐색) (0) | 2023.02.15 |
[알고리즘] 재귀 (0) | 2023.02.13 |