美文网首页
Union-Find

Union-Find

作者: 大橙子CZ | 来源:发表于2016-05-25 23:29 被阅读0次

    Quick Find

    数组的每个位置存相应的节点id,相连接的节点的位置存相同的id。
    判断是否相连(connected)只需判断两位置的id是否相同。
    而将两节点连接起来(union)则需要将第一个节点的id下所有的节点都更改为第二个节点的id。

    public class QuickFind{
        private int[] id;
        public QuickFind(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];
            if(pid == qid){
                return;
            }
            for(int i  = 0;i<id.length;i++){
                if(id[i] == pid){
                    id[i] = qid;
                }
            }
        }
    }
    

    在上述程序中,查找(connected)的时间复杂度为O(1),而union和initialize的时间复杂度均为O(N)。

    Quick Union

    数组的每个位置存的是其父节点;
    判断两个节点是否相连(connected)需判断两节点是否有相同的根节点。
    将两个节点连接(union)则只需将第一个节点的根节点作为第二个节点根节点的孩子即可。

    public class QuickFind{
        private int[] id;
        public QuickFind(int N){
            id = new int[N];
            for(int i = 0;i < N;i++){
                id[i] = i;  
            }
        }
    
        //找根节点
        public int root(int q){
            while(id[q] != q){
                q = id[q];
            }
            return q; 
        }
    
        //判断是否相连
        public boolean connected(int p,int q){
            return root(p)==root(q);
        }
    
        //将两点相连
        public void union(int p,int q){
            id[root(p)] = root(q);
        }
    }
    

    由于查找和归并都需要找根节点,因此这里面所有的方法时间复杂度都为O(N)。

    Quick-Union Improvements

    由于quick union中可能会碰到tall tree,那么寻找其根节点就是一个很耗时的工作。
    改进算法中应用了 weighted algorithm,也就是在连接的时候每次将较小的tree连到较大的tree上,避免让较大的tree在某个tree的底部从而使这个tree变得很长。
    此时需要另一个数组sz存储每个tree的大小。

    public class QuickFind{
        private int[] id;
        private int[] sz;
        public QuickFind(int N){
            id = new int[N];
            sz = new int[N];
            for(int i = 0;i < N;i++){
                id[i] = i;  
                sz[i] = 1;
            }
        }
    
        //找根节点
        public int root(int q){
            while(id[q] != q){
                q = id[q];
            }
            return q; 
        }
    
        //判断是否相连
        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{
                idj] = i;
                sz[i]+=sz[j];
            } 
        }
    }
    

    拥有N个节点的树的最大深度为logN。因此此方法中connected和union的时间复杂度应为O(logN)

    更进一步的改进:path compression

    在搜索根节点时会走过路径上的所有点,莫不如将路径上的所有点都直接加到根节点上。为了时间复杂度的效率及代码的简洁性,考虑将路径上的每一个节点加到其祖父节点(grandparent)下。

    public int root(int q){
            while(id[q] != q){
                id[q] = id[id[q]];//将该节点加到其祖父节点下
                q = id[q];
            }
            return q; 
        }
    

    http://visualgo.net/ufds 展示的为最后path compression的做法(搜索根节点时将路径上每个结点加到根节点上)。

    相关文章

      网友评论

          本文标题:Union-Find

          本文链接:https://www.haomeiwen.com/subject/swytdttx.html