并查集

294 阅读2分钟

十三、并查集

并查集的作用:

  • 可以回答连接问题。

  • 判断网络中结点间的连接状态。

  • 可以实现数学中的集合。

  • 连接问题和路径问题。连接问题比路径问题回答的问题要少。

    • 和二分查找法作比较。
    • 和 select 作比较。
    • 和堆作比较。
  • 判断一个图中有没有环,可以用并查集作为辅助的数据结构。

优化策略:[数据压缩]和[按秩合并]

(一)数据压缩:隔代压缩与完全压缩

  • 隔代压缩性能比较高,虽然压缩不完全,不过多次执行隔代压缩也能达到完全压缩的效果,通常大佬偏向于隔代压缩;
  • 完全压缩需要借助系统栈,使用递归的写法。或者先找到当前结点的根结点,然后把沿途上所有的结点都指向根结点,得遍历两次。

(二)按秩合并

秩也有两种含义:

秩表示以当前结点为根结点的子树结点总数,即这里的「秩」表示 size 含义; 秩表示以当前结点为根结点的子树的高度,即这里的「秩」表示 rank 含义(更合理,因为查询时候的时间性能主要决定于树的高度)。 注:如果同时使用「路径压缩」与「按秩合并」,这里的「秩」就失去了它的定义,但即使秩表示的含义不准确,也能够作为合并时候很好的「参考」。在这种情况下,并查集的查询与合并的时间复杂度可以达到接近 O(1)。

990. 等式方程的可满足性[1]

class Solution {
    public boolean equationsPossible(String[] equations) {
        UnionFind unionFind=new UnionFind(26);
        for(String equation:equations){
            char[]charArray=equation.toCharArray();
            if(charArray[1]=='='){
                int index1=charArray[0]-'a';
                int index2=charArray[3]-'a';
                unionFind.union(index1,index2);
            }
        }
        for(String equation:equations){
            char[]charArray=equation.toCharArray();
            if(charArray[1]=='!'){
                int index1=charArray[0]-'a';
                int index2=charArray[3]-'a';
                if(unionFind.isConnection(index1,index2)){
                    return false;
                }
            }
        }
        return true;
    }
    private class UnionFind{
        private int []parent;
        //构造方法
        public UnionFind(int n){
            parent=new int [n];
            for(int i=0;i<n;i++){
                parent[i]=i;
            }
        }
        //查找
        public int find(int x){
            while(x!=parent[x]){
                parent[x]=parent[parent[x]];
                x=parent[x];
            }
            return x;
        }
        //合并
        public void union(int x,int y){
            int rootX=find(x);
            int rootY=find(y);
            parent[rootX]=rootY;
        }
        //判断是否连接
        public boolean isConnection(int x,int y){
            return find(x)==find(y);
        }

    }
}

684. 冗余连接[2]

class Solution {
    public int[] findRedundantConnection(int[][] edges) {
        int len=edges.length;
        UnionFind unionFind=new UnionFind(len+1);
        for(int[] edge:edges){
            if(unionFind.isConnection(edge[0],edge[1])){
                return edge;
            }
            unionFind.union(edge[0],edge[1]);
        }
        return new int[0];
    }
    //并查集
    private class UnionFind{
        private int []parent;
        //构造方法
        public UnionFind(int n){
            parent=new int [n];
            for(int i=0;i<n;i++){
                parent[i]=i;
            }
        }
        //查找
        public int find(int x){
            while(x!=parent[x]){
                parent[x]=parent[parent[x]];
                x=parent[x];
            }
            return x;
        }
        //合并
        public void union(int x,int y){
            int rootX=find(x);
            int rootY=find(y);
            parent[rootX]=rootY;
        }
        //判断是否连接
        public boolean isConnection(int x,int y){
            return find(x)==find(y);
        }
    }

}

参考资料

[1]

990. 等式方程的可满足性: https://leetcode-cn.com/problems/satisfiability-of-equality-equations/

[2]

684. 冗余连接: https://leetcode-cn.com/problems/redundant-connection/