[알고리즘] Union-Find ( 합집합 찾기)

2022. 2. 15. 19:31·알고리즘
반응형

Union-Find ( 합집합 찾기) : 대표적인 그래프 알고리즘. 서로소 집합( Disjoint-Set) 알고리즘이라고도 한다.

여러 개의 노드가 존재할 때 두 개의 노드를 선택하여,

현재 두 노드가 같은 그래프에 속하는지 판별

-> Find와 Union 연산을 수행할 수 있다.

​

위와 같이 여러 개의 노드가 자유분방하게 존재하고 있다고 했을 때, 이들은 모두 연결되지 않고 각자 자신만을

집합의 원소로 가지고 있을 때 아래와 같이 표현할 수 있다. 모든 값은 자기 자신을 부모 노드로 가지고 있습니다.

이때,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

위와 같이 1과 2가 연결되었을 때, 표로 표현하였을 때 아래의 표로 나타납니다.

노드 번호
1
2
3
4
5
6
7
부모 노드 번호
1
1
3
4
5
6
7

2번째 인덱스의 값에 ' 1 ' 이 들어갑니다. 즉, 노드를 합칠 때 일반적으로 더 작은 쪽의 값으로 합칩니다. 이를 합침(Union)합침(Union)이라고 합니다.

2와 3이 위와 같이 연결되었다면 어떻게 표현될까요?

노드 번호
1
2
3
4
5
6
7
부모 노드 번호
1
1
2
4
5
6
7

다음과 같이 연결됩니다. 근데 한 가지 의아한 점을 확인할 수 있습니다. 바로 ' 1 과 3이 연결되었는지는 어떻게 파악하는가? '입니다.입니다.

1과 3은 부모가 각각 1 과 2로 다르기 때문에 ' 부모 노드 ' 만 보고는 한 번에 파악할 수 없습니다.

​

이때 사용되는 것이 재귀 함수입니다. 입니다.

​

3의 부모를 찾기 위해서 먼저 3이 가리키고 있는 2를 찾습니다. 그럼 2의 부모가 1을 가리키고 있으므로 결과적으로

' 3은 1을 부모가 되는구나! '라고 파악할 수 있습니다.

-> 위와 같은 과정은 재귀적으로 수행될 때 가장 효과적이고 직관적으로 언어를 작성할 수 있습니다.

​

위 작업을 수행하면 결과적으로 ,

노드 번호
1
2
3
4
5
6
7
부모 노드 번호
1
1
1
4
5
6
7

노드 1, 2, 3의 부모가 모두 1이기 때문에 세 가지 노드는 모두 같은 그래프에 속한다고 할 수 있습니다.

​

이렇게 합침(Union) 연산이 수행, 탐색(Find) 연산은 두 개의 노드의 부모 노드를 확인하여 현재 같은 집합에 속하는지 확인하는 알고리즘

​

이것이 Union-Find의 전부입니다!

​

그럼 이것을 C 언어로 구현해 본다면!

 

#include <stdio.h>

// 부모 노드를 탐색합니다.
int getParent(int parent[], int x) {
	if (parent[x] == x) return x;
	else return parent[x] = getParent(parent, parent[x]);
}

// 각 부모 노드를 합칩니다.
int unionParent(int parent[], int a, int b) {
	a = getParent(parent, a);
	b = getParent(parent, b);

	if (a < b) parent[b] = a;
	else parent[a] = b;
}

// 같은 부모 노드를 가지는지 확인합니다.
int findParent(int parent[], int a, int b) {
	a = getParent(parent, a);
	b = getParent(parent, b);

	if (a == b) return 1;
	else return 0;
}

int main(void) {   
	int parent[8];

	for (int i = 1; i <= 7; i++) {
		parent[i] = i;
	}

	unionParent(parent, 1, 2);
	unionParent(parent, 2, 3);
	unionParent(parent, 3, 4);

	unionParent(parent, 5, 6);
	unionParent(parent, 6, 7);

	printf("%d\n", findParent(parent, 1, 3));    // 1
	printf("%d\n", findParent(parent, 1, 5));    // 0
}

이때 만약 위와 같은 예제에

unionParent(parent, 1, 5);

printf("%d\n", findParent(parent, 1, 5));

하면 과연 어떻게 나올까? 바로 공개하자면 1이 출력된다. 즉 1과 5는 부모 노드가 같다는 뜻이다. 이를 표로 표현하자면,

노드 번호
1
2
3
4
5
6
7
부모 노드 번호
1
1
1
1
1
5
5

위와 같이 나온다.

​

그렇다면! 3과 7을 연결하고 결과를 확인하면 어떻게 될까?

unionParent(parent, 3, 7);

printf("%d\n", findParent(parent, 1, 5));

마찬가지로 결과는 1이 출력된다. 이것의 표로 표현하면 마찬가지로 아래와 같이 나온다.

노드 번호
1
2
3
4
5
6
7
부모 노드 번호
1
1
1
1
1
5
5
unionParent(parent, 3, 7);

int unionParent(int parent[], int a, int b) {
	a = getParent(parent, a);
	b = getParent(parent, b);

	if (a < b) parent[b] = a;
	else parent[a] = b;
}

에서 a와 b의 값을 확인해보면 a : 1, b : 5 즉, parent [5] = 1;이 되기 때문이다.

이를 통해 5의 부모 노드 번호가 1이 되고

int findParent(int parent[], int a, int b) {
	a = getParent(parent, a);
	b = getParent(parent, b);

	if (a == b) return 1;
	else return 0;
}

를 통해 비교해보면, 각각 a : 1, b : 1 이 되기 때문에 a == b가 true가 나오므로 1이 출력되는 것이다.

​

자바로도 한번 구현해보자면!

import java.util.Arrays;

public class Study_Union_Find {

	public static int getParent(int[] parent, int x) {
		if(parent[x] == x) return x;
		else return getParent(parent,parent[x]);
	}
	
	public static void UnionParent(int[] parent,int x,int y) {
		int a = getParent(parent, x);
		int b = getParent(parent, y);
		
		if(a < b) parent[b] = a;
		else parent[a] = b;
	}
	
	public static boolean FindParent(int[] parent,int x, int y) {
		int a = getParent(parent, x);
		int b = getParent(parent, y);
		
		if(a == b) return true;
		else return false;
	}
	
	public static void main(String[] args) {
		int[] parent = new int[10];
		
		for(int i = 0; i < 10; i++) 
			parent[i] = i;
		
		UnionParent(parent, 0, 1);
		UnionParent(parent, 1, 2);
		UnionParent(parent, 2, 3);
		UnionParent(parent, 3, 4);
		
		UnionParent(parent, 5, 6);
		UnionParent(parent, 6, 7);
		UnionParent(parent, 7, 8);
		UnionParent(parent, 8, 9);
		
		System.out.println(FindParent(parent, 1, 6));
		
		UnionParent(parent, 4, 9);
		
		System.out.println(FindParent(parent, 1, 6));
		
		System.out.println(Arrays.toString(parent));
	}

}

이러한 Union-Find 알고리즘은 다른 고급 그래프 알고리즘의 베이스가 된다는 점에서 익혀야 하는 알고리즘이다.

다음에 포스팅하게 될 크루스칼 알고리즘(Kruskal Algorithm)은 이 Union-Find를 응용한 알고리즘이다.

 

반응형
저작자표시 비영리 변경금지 (새창열림)

'알고리즘' 카테고리의 다른 글

[알고리즘] 로또의 최고 순위와 최저 순위  (6) 2022.04.23
[알고리즘] 신고 결과 받기  (2) 2022.04.22
[알고리즘] 백준 10989번 문제_수 정렬  (1) 2022.02.10
[알고리즘] 백준 1431번 문제_시리얼 번호  (2) 2022.02.10
[알고리즘] 백준 1181번 문제_단어 정렬  (0) 2022.02.10
'알고리즘' 카테고리의 다른 글
  • [알고리즘] 로또의 최고 순위와 최저 순위
  • [알고리즘] 신고 결과 받기
  • [알고리즘] 백준 10989번 문제_수 정렬
  • [알고리즘] 백준 1431번 문제_시리얼 번호
잉여개발자
잉여개발자
풀스택 개발자를 목표로 잉여롭게 개발 공부도 하면서 다양한 취미 생활도 즐기고 있는 잉여 개발자입니다.
  • 잉여개발자
    잉여로운 개발일지
    잉여개발자
    • 분류 전체보기 (789)
      • 개발정보 (36)
      • 개발환경 (7)
      • 개발생활 (19)
      • React (141)
        • 이론 (23)
        • 기능 (12)
        • 실험실 (88)
        • 버그 (6)
        • 패스트캠퍼스 (9)
        • Npm (3)
      • React Native (28)
        • 공통 (6)
        • TypeScript (3)
        • JavaScript (18)
        • 버그 (1)
      • Next.js (30)
        • 이론 (13)
        • 실험실 (13)
        • 버그 (3)
      • Web (35)
      • 알고리즘 (202)
        • 풀이 힌트 (39)
      • JavaScript (47)
      • TypeScript (29)
        • 기초 (27)
        • 실험실 (2)
      • Node.js (13)
        • 이론 (0)
        • 기능 (3)
        • 실험실 (9)
        • 버그 (1)
      • 도커 (4)
      • CCNA (22)
        • 이론 (4)
        • 문제 (18)
      • 취미생활 (167)
        • 잉여로운 칵테일 (2)
        • 잉여의 식물키우기 (130)
        • 잉여로운 여행기 (11)
        • 잉여의 제2외국어 (21)
        • 잉여로운 책장 (2)
      • Java (1)
        • Java의 정석 (1)
      • 꿀팁 공유 (3)
  • 태그

    Babel
    typescript
    덤프
    영어독학
    식물
    다이소
    자바스크립트
    redux
    ReactNative
    CSS
    타일러영어
    영어회화
    바질
    바질 키우기
    react
    Docker
    리얼클래스
    타입스크립트
    next.js
    리액트
    알고리즘
    javascript
    네이버 부스트캠프
    리얼학습일기
    프로그래머스
    ChatGPT
    네트워크
    webpack
    Node.js
    CCNA
  • hELLO· Designed By정상우.v4.10.1
잉여개발자
[알고리즘] Union-Find ( 합집합 찾기)
상단으로

티스토리툴바