-
1 Week ] Union findIT/알고리즘 공부( Coursera ) 2021. 1. 28. 21:28반응형
코세라 수업을 듣고 내가 정리한 내용이다.
Union find 알고리즘에 대해서 공부한 내용이다.
목차는 아래와 같이 5개이다.
1. dynamic connectivity
2. quick find
3. quick union
4. improvements
#5. applications # Union find 를 활용한 사례
1. dynamic connectivity
먼저 주어지는 N은 노드의 개수로 총 노드의 개수가 주어지고 이 노드들의 연결 정보가 입력값으로 주어진다.
이를 연결해주는 Union이라는 함수와 연결이 되어있는지 검사하는 find,connected 라는 함수를 만들어준다.
대략적인 클래스 모양은 아래와 같다.
public class UF // Union Find UF( int n ) // n개의 objects를 생성 void union(int p, int q) // p,q 사이의 연결을 생성 boolean connected(int p, int q) // p,q가 연결된 컴포넌트 인지 검사 // int find(int p) // int count()
public static void main(String[] args) { int N = StdIn.readInt(); UF uf = new UF(N); while (!StdIn.isEmpty()) { int p = StdIn.readInt(); int q = StdIn.readInt(); if (!uf.connected(p,q,)) { uf.union(p,q); StdOut.println(p+" "+q); } }
2. quick find ( eager algorithm )
예제)
가지고 연결의 index 값이 동일하여 같은 배열에 있으면, 동일한 index를 가지고 있다고 판단하여,
id[] 0,1,1,3,3,0,0,1,3,3 각 최초 시작하는 index 값을 저장해도면 찾을 수 있다.
이 id 배열을 저장하여 두었을 때, 해당 값에 대해서 연결을 물으면 id 값이 다르면 연결성이 없다고 볼 수 있다.
하지만 이 접근방법은 숫자가 커진다면, 바꿔야하는 숫자도 많아져서 문제가 생긴다. 결국 제곱시간은 너무 느리다..
( 쉬운 구현이기 때문에 간단하게는 사용 가능 )
public class QuickFindUF { private int[] id; public QuickFindUF(int N) { id = new int[N]; for (int i = 0; i < N; i++) id[i] = i; } public boolean connected(int p, int q) { return id[p] == id[q]; } public void union(int p, int q) { int pid = id[p]; int qid = id[q]; for (int i = 0; i < id.length; i++) if (id[i] == pid) id[i] = qid; } }
3. quick union ( lazy algorithm )
위의 quick find 알고리즘에서는 union에 많은 시간이 들어간다. ( 거의 무조건 N 만큼은 돌아가야함. )
이를 줄여보고자, quick union 알고리즘으로 만들어 본다.
id 배열에 연결되는 node의 root 값을 찾아가는 방법이므로, union의 속도가 빨라질수 있다.
이 방법은 결국 들어오는 왼쪽의 노드를 오른쪽의 노드 밑에 붙이는 만드는 방법으로 계속 트리가 늘어날 수있다.
public class QuickUnionUF { private int[] id; public QuickUnionUF( int N ) { id = new int[N]; for (int i=0;i<N;i++) id[i]=i; } private int root(int i) { while (i != id[i]) i= id[i]; return i; } public boolean connected(int p, int q) { return root(p)==root(q); } public void union(int p, int q) { int i = root(p); int j = root(q); id[i]=j; } }
이 알고리즘은 quick find 보다는 빠를 수 있지만, quick union도 너무 느린 알고리즘이다.
Algorithm initialize union find quick-find N N 1 quick-union N N N ( worst case ) quick-find의 단점은 union 작업시 N 만큼 걸리는 것이고,
quick-union의 경우 find 작업시 최대 N 만큼 걸릴 수 있게 되어 속도가 둘 다 좋지는 않다. ( 아주 긴 tree 모양이 될 때,)
그래서 quick-union을 개선시킨 방향을 생각해본다.
4. quick-union improvements
개선방향으로는 union 함수를 수행할 때, weighting을 비교하여 tree가 길어지지 않게 만드는 방법이다.
이렇게 되면, tree는 한쪽으로 치우쳐지지 않고, 분배되는 tree로 만들어지게 된다.
public class QuickUnionUF { private int[] id; private int[] sz; public QuickUnionUF( int N ) { id = new int[N]; for (int i=0;i<N;i++) { id[i]=i; sz[i]=1;} } private int root(int i) { while (i != id[i]) i= id[i]; return i; } public boolean connected(int p, int q) { return root(p)==root(q); } public void union(int p, int q) { int i = root(p); int j = root(q); // 추가된 코드 if (i==j) return; if (sz[i]<sz[j]) { id[i] = j; sz[j]+= sz[i];} else { id[j] = i; sz[i]+= sz[j];} } }
Algorithm initialize union find quick-find N N 1 quick-union N N N ( worst case ) quick-union improvements N log N log N 여기서 좀 더 발전적인 방향으로 path compression ( 루트를 바로 상속받기 ) 방법으로 좀 더 줄일 수 있다.
현재는 한 비교되는 노드에 자식으로 들어가는데, 각자 최고 root에 대해 자식으로 들어가게 되면
더 평평하게 만들 수 있다. 아래는 root을 찾을 때 추가되는 코드이다.
private int root(int i) { while (i!=id[i]) { id[i]=id[id[i]]; i=id[i] } return i; }
반응형'IT > 알고리즘 공부( Coursera )' 카테고리의 다른 글
5week] Balanced Search Trees (0) 2021.04.02