Data Structure – Union-Find (경로압축 및 가중법칙)

Union-Find (유니온 파인드)

Union: 원소 x가 속한 부분 집합과 원소 y가 속한 부분 집합의 합집합을 구한다.
Find: 원소 x가 속한 부분 집합을 찾는다. (루트 반환)

Union의 가중법칙: 무거운 쪽이 루트가 되도록 하는 방식, 노드 개수가 더 많은 트리에 연결한다.
Find의 경로압축: Find를 수행할 때 x에서 루트까지 가는 경로 상에 있는 노드를 모두 루트의 자식 노드로 만든다.

가중법칙 및 경로압축을 하고 Union을 n-1회, find를 m(>=n)회 하면 대략적으로 O(m)을 만족한다.
위 최적화를 하지 않을 시, 최악의 경우 편향 트리가 만들어져 Find를 1회하는데 O(n)이 소요된다.


Union-Find 최적화 및 비최적화 비교 소스 (Comapare with Optimization & non-optimization)

#include <stdio.h>

#define size 7
typedef struct {
	int parent;
	int count; // '최적화' 에서만 사용
}uf;
typedef uf* ufp;

// 초기 집합 n개 생성
void set_init(ufp u, int n) {
	for (int i = 0; i < n; i++) {
		u[i].parent = -1;
		u[i].count = 1;
	}
}

// curr가 속하는 집합의 루트을 반환한다.
int Find(ufp u, int curr) {
	if (u[curr].parent == -1)
		return curr;
	return Find(u, u[curr].parent);
}

// 두 개의 원소가 속한 집합을 합친다.
void Union(ufp u, int a, int b) {
	int root1 = Find(u, a);
	int root2 = Find(u, b);
	if (root1 != root2)
		u[root2].parent = root1;
}

// Find를 한 뒤 경로압축을 진행한다. 
int comp_Find(ufp u, int curr) {
	if (u[curr].parent == -1)
		return curr;

	// 루트 노드 찾기 
	int tmp = comp_Find(u, u[curr].parent);
	// 경로 압축 (리턴해서 받은 루트 노드의 값을 부모 노드의 값으로 사용)
	// 다음에 comp_Find가 호출되면 순환 횟수가 줄어든다. 
	u[curr].parent = tmp;
	return tmp;
}

// 경로압축과 가중법칙을 적용하여 Union을 한다. 
void fast_Union(ufp u, int a, int b) {
	int root1 = comp_Find(u, a);
	int root2 = comp_Find(u, b);
	int temp = u[root1].count + u[root2].count;

	if (root1 != root2) {
		if (u[root1].count < u[root2].count) {
			u[root1].parent = root2;
			u[root2].count = temp;
		}
		else {
			u[root2].parent = root1;
			u[root1].count = temp;
		}
	}
}

int main() {
	// -----------------비 최적화 코드 사용-------------------------- // 

	// 집합 size개 생성 
	ufp u = (ufp)malloc(sizeof(uf) * size);
	set_init(u, size);

	Union(u, 0, 1); // {0,1},{2},{3},{4},{5},{6}
	Union(u, 2, 3); // {0,1},{2,3},{4},{5},{6}
	Union(u, 4, 5); // {0,1},{2,3},{4,5},{6}
	Union(u, 1, 2); // {0,1,2,3},{4,5},{6}
	Union(u, 3, 4); // {0,1,2,3,4,5},{6}
	Union(u, 5, 6); // {0,1,2,3,4,5,6} 

	// 합집합 결과 보기 <가중 법칙 적용 x>
	printf("\n(비최적화) 합 집합 결과: ");
	for (int i = 0; i < size; i++)
		printf("[%d] ", u[i].parent);
	printf("\n");

	free(u);

	// -----------------최적화 코드 사용-------------------------- // 

	// 집합 size개 생성 
	u = (ufp)malloc(sizeof(uf) * size);
	set_init(u, size);

	fast_Union(u, 0, 1); // {0,1},{2},{3},{4},{5},{6}
	fast_Union(u, 2, 3); // {0,1},{2,3},{4},{5},{6}
	fast_Union(u, 4, 5); // {0,1},{2,3},{4,5},{6}
	fast_Union(u, 1, 2); // {0,1,2,3},{4,5},{6}
	fast_Union(u, 3, 4); // {0,1,2,3,4,5},{6}
	fast_Union(u, 5, 6); // {0,1,2,3,4,5,6} 

	// 합집합 결과 보기 <경로압축 및 가중 법칙 적용>
	printf("\n(최적화) 합 집합 결과: ");
	for (int i = 0; i < size; i++)
		printf("[%d] ", u[i].parent);
	printf("\n");

	free(u);
	return 0;
}

Leave a Reply

Your email address will not be published. Required fields are marked *