天天看點

并查集路徑壓縮

并查集裡的 find 函數裡可以進行路徑壓縮,是為了更快速的查找一個點的根節點。對于一個集合樹來說,它的根節點下面可以依附着許多的節點,是以,我們可以嘗試在 find 的過程中,從底向上,如果此時通路的節點不是根節點的話,那麼我們可以把這個節點盡量的往上挪一挪,減少數的層數,這個過程就叫做路徑壓縮。

如下圖中,find(4) 的過程就可以路徑壓縮,讓樹的層數更少。

并查集路徑壓縮

節點 4 往上尋找根節點時,壓縮第一步,樹的層數就減少了一層:

并查集路徑壓縮

節點 2 向上尋找,也不是根節點,那麼把元素 2 指向原來父節點的父節點,操後後樹的層數相應減少了一層,同時傳回根節點 0。

并查集路徑壓縮

find 過程代碼修改為 :

// 查找過程, 查找元素p所對應的集合編号

// O(h)複雜度, h為樹的高度

private int find(int p){

    assert( p >= 0 && p < count );

    // path compression 1

    while( p != parent[p] ){

        parent[p] = parent[parent[p]];

        p = parent[p];

    }

    return p;

}

上述路徑壓縮并不是最優的方式,我們可以把最初的樹壓縮成下圖所示,層數是最少的。

并查集路徑壓縮

這個 find 過程代表表示為:

...

private int find(int p) {

    assert (p >= 0 && p < count);

    //第二種路徑壓縮算法

    if (p != parent[p])

        parent[p] = find(parent[p]);

    return parent[p];

源碼包下載下傳:Download

package runoob.union;

/**

 * 基于rank的優化

 */

public class UnionFind4 {

    private int[] rank;   // rank[i]表示以i為根的集合所表示的樹的層數

    private int[] parent; // parent[i]表示第i個元素所指向的父節點

    private int count;    // 資料個數

    // 構造函數

    public UnionFind4(int count){

        rank = new int[count];

        parent = new int[count];

        this.count = count;

        // 初始化, 每一個parent[i]指向自己, 表示每一個元素自己自成一個集合

        for( int i = 0 ; i < count ; i ++ ){

            parent[i] = i;

            rank[i] = 1;

        }

    // 查找過程, 查找元素p所對應的集合編号

    // O(h)複雜度, h為樹的高度

    private int find(int p){

        assert( p >= 0 && p < count );

        // 不斷去查詢自己的父親節點, 直到到達根節點

        // 根節點的特點: parent[p] == p

        while( p != parent[p] )

            p = parent[p];

        return p;

        //第二種路徑壓縮算法

        //if( p != parent[p] )

        //parent[p] = find( parent[p] );

        //return parent[p];

    // 檢視元素p和元素q是否所屬一個集合

    public boolean isConnected( int p , int q ){

        return find(p) == find(q);

    // 合并元素p和元素q所屬的集合

    public void unionElements(int p, int q){

        int pRoot = find(p);

        int qRoot = find(q);

        if( pRoot == qRoot )

            return;

        if( rank[pRoot] < rank[qRoot] ){

            parent[pRoot] = qRoot;

        else if( rank[qRoot] < rank[pRoot]){

            parent[qRoot] = pRoot;

        else{ // rank[pRoot] == rank[qRoot]

            rank[qRoot] += 1;   // 維護rank的值