並查集及代碼實現

1. 並查集

1.1 並查集的概念

並查集也是一種數據結構,主要有兩個操作,集合的合併,判斷兩個元素是否屬於同一個集合,是一種邏輯結構。

1.2 並查集的實現思路

並查集的底層實現有多重方式,可以使用數組、List、樹等等,選擇的依據是哪種實現方式可以讓操作的複雜度小。可以使用一種特殊的樹來實現並查集如圖,每一個結點都有一個父結點,最後一個結點的父節點指向自己,也是這個集合的代表結點,判斷兩個元素是否屬於同一個集合,就是判斷這兩個元素所在集合的代表結點是否相等,合併兩個集合,讓結點數少的集合,掛載結點數多的代表結點的下面。
在這裏插入圖片描述
具體實現時,可以使用Map,使操作更簡單。

1.3 代碼

public class Code_09_UnionFind {
    
    public static class Node{
		// whatever you like
    }

    public static class DisjointSets{
        //定義兩個Map,fatherMap<Node, fatherNode>, 存儲結點和他的父節點,sizeMap<Node, Integer>
        //存儲結點和以這個結點爲父節點的結點個數,並且,只有當這個結點是代表結點的時候,sizeMap中的
        //value值纔有意義
        public Map<Node, Node> fatherMap;
        public Map<Node, Integer> sizeMap;

        public DisjointSets(List<Node> list){
            fatherMap = new HashMap<>();
            sizeMap = new HashMap<>();
            makeSets(list);
        }

        //初始化並查集,使用並查集的條件是,必須要知道所有元素
        //開始的時候,每個結點的父節點都是自己。
        public void makeSets(List<Node> list){
            fatherMap.clear();
            sizeMap.clear();
            for(Node node : list){
                fatherMap.put(node, node);
                sizeMap.put(node, 1);
            }
        }

        //根據一個元素,查找這個元素所在的集合的代表結點,並且,將這個集合壓縮,就是從這個結點
        //到代表結點之間的所有節點的父節點,都指向代表結點
        public Node findFather(Node node){
            Node father = fatherMap.get(node);
            if(father != node){
                //這裏使用的是遞歸,如果node的父節點不是father,就找父節點的父節點,直到找到
                //代表結點,node是一直在變的
                father = findFather(father);
            }
            //找到代表結點之後,將每個結點的父節點設爲代表結點
            fatherMap.put(node, father);
            //返回father
            return father;
        }

        //判斷兩個元素是否屬於同一個集合
        public boolean isSameSet(Node node1, Node node2){
            return findFather(node1) == findFather(node2);
        }

        //合併兩個集合
        public void union(Node a, Node b){
            if(a == null || b == null) return;
            Node aFather = findFather(a);
            Node bFather = findFather(b);
            if(aFather != bFather){
                int aFatherSize = sizeMap.get(aFather);
                int bFatherSize = sizeMap.get(bFather);
                if( aFatherSize <= bFatherSize){
                    //如果a所在集合結點數少於b的,就將a的父節點設爲b的父節點,a掛載b後頭
                    fatherMap.put(aFather, bFather);
                    //修改個數
                    sizeMap.put(bFather, aFatherSize + bFatherSize);
                }else{
                    fatherMap.put(bFather, aFather);
                    sizeMap.put(aFather, aFatherSize + bFatherSize);
                }
            }
        }
    }
}

時間複雜度:假設有n個元素,如果查詢的次數(查找代表結點)加上合併的次數的數量級達到或者超過了O(n),那麼,單次查詢或者單次合併,平均複雜度爲O(1)。證明過程很複雜,略。

如有不足之處,歡迎指正,謝謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章