並查集java實現

zw147258369發表於2020-11-06

並查集定義

並查集是一種樹型的資料結構,用於處理一些不相交集合(disjoint sets)的合併及查詢問題。常常在使用中以森林來表示。

借鑑一個看到的故事

江湖上散落著各式各樣的大俠,有上千個之多。他們沒有什麼正當職業,整天揹著劍在外面走來走去,碰到和自己不是一路人的,就免不了要打一架。但大俠們有一個優點就是講義氣,絕對不打自己的朋友。而且他們信奉“朋友的朋友就是我的朋友”,只要是能通過朋友關係串聯起來的,不管拐了多少個彎,都認為是自己人。這樣一來,江湖上就形成了一個一個的幫派,通過兩兩之間的朋友關係串聯起來。而不在同一個幫派的人,無論如何都無法通過朋友關係連起來,於是就可以放心往死了打。但是兩個原本互不相識的人,如何判斷是否屬於一個朋友圈呢?

我們可以在每個朋友圈內推舉出一個比較有名望的人,作為該圈子的代表人物。這樣,每個圈子就可以這樣命名“中國同胞隊”美國同胞隊”……兩人只要互相對一下自己的隊長是不是同一個人,就可以確定敵友關係了。

但是還有問題啊,大俠們只知道自己直接的朋友是誰,很多人壓根就不認識隊長要判斷自己的隊長是誰,只能漫無目的的通過朋友的朋友關係問下去:“你是不是隊長?你是不是隊長?”這樣,想打一架得先問個幾十年,餓都餓死了,受不了。這樣一來,隊長面子上也掛不住了,不僅效率太低,還有可能陷入無限迴圈中。

於是隊長下令,重新組隊。隊內所有人實行分等級制度,形成樹狀結構,我隊長就是根節點,下面分別是二級隊員、三級隊員。每個人只要記住自己的上級是誰就行了。遇到判斷敵友的時候,只要一層層向上問,直到最高層,就可以在短時間內確定隊長是誰了。由於我們關心的只是兩個人之間是否是一個幫派的,至於他們是如何通過朋友關係相關聯的,以及每個圈子內部的結構是怎樣的,甚至隊長是誰,都不重要了。所以我們可以放任隊長隨意重新組隊,只要不搞錯敵友關係就好了。於是,門派產生了。

程式碼實現:

在程式碼中,通常使用一個 parent陣列表示所有元素,陣列下標表示具體每一個元素,每個陣列值表示該元素對應的父節點(也就是它的上級),如果該元素是根節點或者是單獨的節點,該元素的值為自己本身,如 parent[3] = 3

例子:

 

 


public class QuickFindUF {
    private int[] parent = new int[5]; //定義長度為5的陣列,表示每個元素

    public void init() {  //初始化陣列,開始時,每個元素指向自己
        for(int i = 0; i < parent.length; i++) {
            parent[i] = i;
        }
    }

    private int root(int i) {  //找到節點i的根節點
        while(i != parent[i]) {
            i = parent[i];
        }
        return i;
    }

    public void union(int p, int q) {  //快速合併p q兩個節點:先找到兩個根節點,然後將其中一個根節點指向另一個根節點
        int proot = root(p);
        int qroot = root(q);
        parent[proot] = qroot;
    }

   public boolean connected(int p, int q) {  //判斷節點p和q是否相連
        return root(p) == root(q);
    }


    public static void main(String[] args) {
        init();
        union(0, 2);
        union(1, 4);
    }
 



}

典型應用:

並查集常常用來判斷在一個圖中是否存在迴路(是否可以生成樹),以及用來判斷圖的聯通性問題

 

684. 冗餘連線

在本問題中, 樹指的是一個連通且無環的無向圖。

輸入一個圖,該圖由一個有著N個節點 (節點值不重複1, 2, ..., N) 的樹及一條附加的邊構成。附加的邊的兩個頂點包含在1到N中間,這條附加的邊不屬於樹中已存在的邊。

結果圖是一個以組成的二維陣列。每一個的元素是一對[u, v] ,滿足 u < v,表示連線頂點u 和v無向圖的邊。

返回一條可以刪去的邊,使得結果圖是一個有著N個節點的樹。如果有多個答案,則返回二維陣列中最後出現的邊。答案邊 [u, v] 應滿足相同的格式 u < v

示例 1:

輸入: [[1,2], [1,3], [2,3]]
輸出: [2,3]
解釋: 給定的無向圖為:
  1
 / \
2 - 3

示例 2:

輸入: [[1,2], [2,3], [3,4], [1,4], [1,5]]
輸出: [1,4]
解釋: 給定的無向圖為:
5 - 1 - 2
    |   |
    4 - 3

注意:

  • 輸入的二維陣列大小在 3 到 1000。
  • 二維陣列中的整數在1到N之間,其中N是輸入陣列的大小。
    class Solution {
        public int[] parent;
        public int[] findRedundantConnection(int[][] edges) {
            parent = new int[edges.length + 1];
            init(edges.length);
            for(int i = 0; i < edges.length; i++) {
                if(connected(edges[i][0], edges[i][1])) {
                    return edges[i];
                } else {
                    union(edges[i][0], edges[i][1]);
                }
            }
            return edges[edges.length];
    
        }
    
        private int root(int i) {
            while(i != parent[i]) {
                i = parent[i];
            }
            return i;
        }
        public boolean connected(int p, int q) {
            return root(p) == root(q);
        }
        public void union(int p, int q) {
            int proot = root(p);
            int qroot = root(q);
            parent[proot] = qroot;
        }
        public void init(int N) {
            for(int i = 1; i <= N; i++) {
                parent[i] = i;
            }
        }
    }

     

相關文章