九、自己動手實現------------“ Union Find 並查集 ”

參考文章:

https://www.cnblogs.com/gaoquanquan/p/9898624.html          java——並查集 UnionFind (強推

http://www.cnblogs.com/noKing/p/8018609.html#_label0         並查集(Java實現)

https://github.com/liuyubobobo/Play-with-Algorithms  


溫馨提示:

       如果對於看不懂的代碼,強烈建議拿出紙和筆,來畫一畫圖,跟着代碼走一遍,一遍走不通,過一會再捋一捋。如果實在是捋不順,就找個師傅好好教教。

上面的幾篇文章,有參考的實現代碼,有並查集的原理,我這裏就不廢話,直接上代碼,看原理,建議看視頻,以及優質的博客。

 

 並查集  Union Find 接口定義如下:

public interface UF {
    int getSize();
    boolean isConnected(int p, int q);
    void unionElements(int p, int q);
}

 

並查集的實現,這裏有6個版本,每一個版本,都有所改進,是逐步的改進,這點 波波老師  的視頻做的非常的好,我就無恥的拿過來了,因爲我也想不出更好的代碼了,至少目前是這樣┭┮﹏┭┮

 

第一版:

// 我們的第一版Union-Find
public class UnionFind1 implements UF {

    private int[] id;    // 我們的第一版Union-Find本質就是一個數組

    public UnionFind1(int size) {

        id = new int[size];

        // 初始化, 每一個id[i]指向自己, 沒有合併的元素
        for (int i = 0; i < size; i++)
            id[i] = i;
    }

    @Override
    public int getSize(){
        return id.length;
    }

    // 查找元素p所對應的集合編號
    // O(1)複雜度
    private int find(int p) {
        if(p < 0 || p >= id.length)
            throw new IllegalArgumentException("p is out of bound.");

        return id[p];
    }

    // 查看元素p和元素q是否所屬一個集合
    // O(1)複雜度
    @Override
    public boolean isConnected(int p, int q) {
        return find(p) == find(q);
    }

    // 合併元素p和元素q所屬的集合
    // O(n) 複雜度
    @Override
    public void unionElements(int p, int q) {

        int pID = find(p);
        int qID = find(q);

        if (pID == qID)
            return;

        // 合併過程需要遍歷一遍所有元素, 將兩個元素的所屬集合編號合併
        for (int i = 0; i < id.length; i++)
            if (id[i] == pID)
                id[i] = qID;
    }
}

 

 

第二版:

// 我們的第二版Union-Find
public class UnionFind2 implements UF {

    // 我們的第二版Union-Find, 使用一個數組構建一棵指向父節點的樹
    // parent[i]表示第一個元素所指向的父節點
    private int[] parent;

    // 構造函數
    public UnionFind2(int size){

        parent = new int[size];

        // 初始化, 每一個parent[i]指向自己, 表示每一個元素自己自成一個集合
        for( int i = 0 ; i < size ; i ++ )
            parent[i] = i;
    }

    @Override
    public int getSize(){
        return parent.length;
    }

    // 查找過程, 查找元素p所對應的集合編號
    // O(h)複雜度, h爲樹的高度
    private int find(int p){
        if(p < 0 || p >= parent.length)
            throw new IllegalArgumentException("p is out of bound.");

        // 不斷去查詢自己的父親節點, 直到到達根節點
        // 根節點的特點: parent[p] == p
        while(p != parent[p])
            p = parent[p];
        return p;
    }

    // 查看元素p和元素q是否所屬一個集合
    // O(h)複雜度, h爲樹的高度
    @Override
    public boolean isConnected( int p , int q ){
        return find(p) == find(q);
    }

    // 合併元素p和元素q所屬的集合
    // O(h)複雜度, h爲樹的高度
    @Override
    public void unionElements(int p, int q){

        int pRoot = find(p);
        int qRoot = find(q);

        if( pRoot == qRoot )
            return;

        parent[pRoot] = qRoot;
    }
}

 

第三版:

// 我們的第三版Union-Find
public class UnionFind3 implements UF{

    private int[] parent; // parent[i]表示第一個元素所指向的父節點
    private int[] sz;     // sz[i]表示以i爲根的集合中元素個數

    // 構造函數
    public UnionFind3(int size){

        parent = new int[size];
        sz = new int[size];

        // 初始化, 每一個parent[i]指向自己, 表示每一個元素自己自成一個集合
        for(int i = 0 ; i < size ; i ++){
            parent[i] = i;
            sz[i] = 1;
        }
    }

    @Override
    public int getSize(){
        return parent.length;
    }

    // 查找過程, 查找元素p所對應的集合編號
    // O(h)複雜度, h爲樹的高度
    private int find(int p){
        if(p < 0 || p >= parent.length)
            throw new IllegalArgumentException("p is out of bound.");

        // 不斷去查詢自己的父親節點, 直到到達根節點
        // 根節點的特點: parent[p] == p
        while( p != parent[p] )
            p = parent[p];
        return p;
    }

    // 查看元素p和元素q是否所屬一個集合
    // O(h)複雜度, h爲樹的高度
    @Override
    public boolean isConnected( int p , int q ){
        return find(p) == find(q);
    }

    // 合併元素p和元素q所屬的集合
    // O(h)複雜度, h爲樹的高度
    @Override
    public void unionElements(int p, int q){

        int pRoot = find(p);
        int qRoot = find(q);

        if(pRoot == qRoot)
            return;

        // 根據兩個元素所在樹的元素個數不同判斷合併方向
        // 將元素個數少的集合合併到元素個數多的集合上
        if(sz[pRoot] < sz[qRoot]){
            parent[pRoot] = qRoot;
            sz[qRoot] += sz[pRoot];
        }
        else{ // sz[qRoot] <= sz[pRoot]
            parent[qRoot] = pRoot;
            sz[pRoot] += sz[qRoot];
        }
    }
}

 

第四版:

// 我們的第四版Union-Find
public class UnionFind4 implements UF {

    private int[] rank;   // rank[i]表示以i爲根的集合所表示的樹的層數
    private int[] parent; // parent[i]表示第i個元素所指向的父節點

    // 構造函數
    public UnionFind4(int size){

        rank = new int[size];
        parent = new int[size];

        // 初始化, 每一個parent[i]指向自己, 表示每一個元素自己自成一個集合
        for( int i = 0 ; i < size ; i ++ ){
            parent[i] = i;
            rank[i] = 1;
        }
    }

    @Override
    public int getSize(){
        return parent.length;
    }

    // 查找過程, 查找元素p所對應的集合編號
    // O(h)複雜度, h爲樹的高度
    private int find(int p){
        if(p < 0 || p >= parent.length)
            throw new IllegalArgumentException("p is out of bound.");

        // 不斷去查詢自己的父親節點, 直到到達根節點
        // 根節點的特點: parent[p] == p
        while(p != parent[p])
            p = parent[p];
        return p;
    }

    // 查看元素p和元素q是否所屬一個集合
    // O(h)複雜度, h爲樹的高度
    @Override
    public boolean isConnected( int p , int q ){
        return find(p) == find(q);
    }

    // 合併元素p和元素q所屬的集合
    // O(h)複雜度, h爲樹的高度
    @Override
    public void unionElements(int p, int q){

        int pRoot = find(p);
        int qRoot = find(q);

        if( pRoot == qRoot )
            return;

        // 根據兩個元素所在樹的rank不同判斷合併方向
        // 將rank低的集合合併到rank高的集合上
        if(rank[pRoot] < rank[qRoot])
            parent[pRoot] = qRoot;
        else if(rank[qRoot] < rank[pRoot])
            parent[qRoot] = pRoot;
        else{ // rank[pRoot] == rank[qRoot]
            parent[pRoot] = qRoot;
            rank[qRoot] += 1;   // 此時, 我維護rank的值
        }
    }
}

 

 

第五版:

// 我們的第五版Union-Find
public class UnionFind5 implements UF {

    // rank[i]表示以i爲根的集合所表示的樹的層數
    // 在後續的代碼中, 我們並不會維護rank的語意, 也就是rank的值在路徑壓縮的過程中, 有可能不在是樹的層數值
    // 這也是我們的rank不叫height或者depth的原因, 他只是作爲比較的一個標準
    private int[] rank;
    private int[] parent; // parent[i]表示第i個元素所指向的父節點

    // 構造函數
    public UnionFind5(int size){

        rank = new int[size];
        parent = new int[size];

        // 初始化, 每一個parent[i]指向自己, 表示每一個元素自己自成一個集合
        for( int i = 0 ; i < size ; i ++ ){
            parent[i] = i;
            rank[i] = 1;
        }
    }

    @Override
    public int getSize(){
        return parent.length;
    }

    // 查找過程, 查找元素p所對應的集合編號
    // O(h)複雜度, h爲樹的高度
    private int find(int p){
        if(p < 0 || p >= parent.length)
            throw new IllegalArgumentException("p is out of bound.");

        while( p != parent[p] ){
            parent[p] = parent[parent[p]];
            p = parent[p];
        }
        return p;
    }

    // 查看元素p和元素q是否所屬一個集合
    // O(h)複雜度, h爲樹的高度
    @Override
    public boolean isConnected( int p , int q ){
        return find(p) == find(q);
    }

    // 合併元素p和元素q所屬的集合
    // O(h)複雜度, h爲樹的高度
    @Override
    public void unionElements(int p, int q){

        int pRoot = find(p);
        int qRoot = find(q);

        if( pRoot == qRoot )
            return;

        // 根據兩個元素所在樹的rank不同判斷合併方向
        // 將rank低的集合合併到rank高的集合上
        if( rank[pRoot] < rank[qRoot] )
            parent[pRoot] = qRoot;
        else if( rank[qRoot] < rank[pRoot])
            parent[qRoot] = pRoot;
        else{ // rank[pRoot] == rank[qRoot]
            parent[pRoot] = qRoot;
            rank[qRoot] += 1;   // 此時, 我維護rank的值
        }
    }
}

 

 

第六版:

// 我們的第六版Union-Find
public class UnionFind6 implements UF {

    // rank[i]表示以i爲根的集合所表示的樹的層數
    // 在後續的代碼中, 我們並不會維護rank的語意, 也就是rank的值在路徑壓縮的過程中, 有可能不在是樹的層數值
    // 這也是我們的rank不叫height或者depth的原因, 他只是作爲比較的一個標準
    private int[] rank;
    private int[] parent; // parent[i]表示第i個元素所指向的父節點

    // 構造函數
    public UnionFind6(int size){

        rank = new int[size];
        parent = new int[size];

        // 初始化, 每一個parent[i]指向自己, 表示每一個元素自己自成一個集合
        for( int i = 0 ; i < size ; i ++ ){
            parent[i] = i;
            rank[i] = 1;
        }
    }

    @Override
    public int getSize(){
        return parent.length;
    }

    // 查找過程, 查找元素p所對應的集合編號
    // O(h)複雜度, h爲樹的高度
    private int find(int p){
        if(p < 0 || p >= parent.length)
            throw new IllegalArgumentException("p is out of bound.");

        // path compression 2, 遞歸算法
        if(p != parent[p])
            parent[p] = find(parent[p]);
        return parent[p];
    }

    // 查看元素p和元素q是否所屬一個集合
    // O(h)複雜度, h爲樹的高度
    @Override
    public boolean isConnected( int p , int q ){
        return find(p) == find(q);
    }

    // 合併元素p和元素q所屬的集合
    // O(h)複雜度, h爲樹的高度
    @Override
    public void unionElements(int p, int q){

        int pRoot = find(p);
        int qRoot = find(q);

        if( pRoot == qRoot )
            return;

        // 根據兩個元素所在樹的rank不同判斷合併方向
        // 將rank低的集合合併到rank高的集合上
        if( rank[pRoot] < rank[qRoot] )
            parent[pRoot] = qRoot;
        else if( rank[qRoot] < rank[pRoot])
            parent[qRoot] = pRoot;
        else{ // rank[pRoot] == rank[qRoot]
            parent[pRoot] = qRoot;
            rank[qRoot] += 1;   // 此時, 我維護rank的值
        }
    }
}

 

 

 

 

 

 

 

 

 

 

 

 

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