알고리즘/graph

유니온 파인드

씩씩한 IT블로그 2020. 6. 11. 16:36
반응형

1. 함수의 구성

유니온 파인드는 find와 union 두가지 함수로 구성이 된다.

(1) find는 그래프에서 루트노드를 찾아주는 함수이다.

int find(int x) {
	if (parent[x] == x) {
		return x;
	}
	else {
		return parent[x] = find(parent[x]);
	}
}

 

(2) union은 서로 연결되어 있지 않은 그래프를 연결시키는 함수이다.

void Union(int x, int y){
	x = find(x);
	y = find(y);
	if(x!=y){
		parent[y] = x;
	}
}

 

 

2. 함수의 사용

유니온 파인드는 아래의 두가지 경우에 사용할 수 있다.

(1) 그래프가 연결되어 있는지 확인할 때 (ex [boj]1717_집합의 표현)

어떤 두 노드의 루트노드가 같다면 두 노드는 한 그래프 안에 있음을 보장받는다.

#include <iostream>
#include <algorithm>
#include <string>

using namespace std;

int parent[1000001];


int find(int x) {
	if (parent[x] == x) {
		return x;
	}
	else {
		return parent[x] = find(parent[x]);
	}
}

void uni(int x, int y) {
	x = find(x);
	y = find(y);
	
	parent[x] = y;
}

int main() {
	cin.tie(0);
	cout.tie(0);
	int n, m;
	int op, a, b;

	cin >> n >> m;
	for (int i = 0; i < n+1; i++) {
		parent[i] = i;
	}

	for (int i = 0; i < m; i++) {
		cin >> op >> a >> b;
		if (op == 1) {
			if (find(a) == find(b)) { //루트노드가 같다면?!
				cout << "YES\n";
			}
			else {
				cout << "NO\n";
			}
		}
		else {
			uni(a, b);
		}
	}

	return 0;
}

 

(2) 사이클이 존재하는지를 확인할 때. (ex [boj]1922_네트워크연결)

크루스칼 알고리즘에서는 두 노드를 연결했을때 사이클이 생기는지 확인해야 하는 순간이 있다. 이때 유니온 파인드를 이용할 수 있다.

어떠한 두 노드의 루트노드가 같다는 것은 두 노드가 한 그래프로 이어져 있다는 것을 뜻한다.

이때 두 노드를 연결하면 닫힌 그래프, 즉 사이클이 생긴다.

따라서 루트노드가 같은 두 노드를 연결하면 사이클이 생긴다는 것을 알 수 있다.

#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>

using namespace std;

int parent[1001];

int find(int x) {
	if (x == parent[x]) {
		return x;
	}
	else {
		return parent[x] = find(parent[x]);
	}
}

void uni(int x, int y) {
	x = find(x);
	y = find(y);
	parent[x] = y;
}

bool cmp(vector<int> v1, vector<int> v2) {
	if (v1[2] < v2[2]) {
		return true;
	}
	else {
		return false;
	}
	//return v1[2] < v2[2];
}

int main() {
	vector<vector<int>> node;
	int n,m;
	cin >> n >> m;
	stack<int> s;

	int a, b, c;
	for (int i = 0; i < m; i++) {
		cin >> a >> b >> c;
		node.push_back({ a,b,c });
	}

	for (int i = 1; i <= n; i++) {
		parent[i] = i;
	}
	
	sort(node.begin(), node.end(), cmp);

	int temp1, temp2, cnt=0, ans=0;
	for (int i = 0; i < node.size(); i++) {
		temp1 = find(node[i][0]);
		temp2 = find(node[i][1]);
		// 루트노드가 다르다면 (연결해도 사이클이 생기지 않는다면)
		if (temp1 != temp2) {
			uni(temp1, temp2);
			cnt++;
			ans += node[i][2];
			// 노드개수-1 = 간선개수
			if (cnt == n - 1) {
				break;
			}
		}
	}
	cout << ans;

	return 0;
}

 

* 싸이클이 생기는지 확인하는 또다른 방법은 stack을 이용하는 것이다. 아래를 참조한다

https://blog.naver.com/ngoodsamari?Redirect=Log&logNo=221607542218&from=postView

 

반응형