ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 1 Week ] Union find
    IT/알고리즘 공부( 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

    댓글

Designed by Tistory.