並查集演算法詳解

2021-10-01 08:18:24 字數 1748 閱讀 4226

並查集演算法,主要是解決圖論中「動態連通性」問題的。

一、簡單介紹

union-find 演算法主要需要實現這兩個 api

class uf
二、基本思路

我們使用森林(若干棵樹)來表示圖的動態連通性,用陣列來具體實現這個森林。

怎麼用森林來表示連通性呢?我們設定樹的每個節點有乙個指標指向其父節點,如果是根節點的話,這個指標指向自己。

clsss uf(int n)

/* 返回某個節點 x 的根節點 */

private int find(int x)

return x;

}/* 返回當前的連通分量個數 */

public int count()

如果節點p和q連通的話,它們一定擁有相同的根節點

public boolean connection(int p,int q)
分析:

主要 api的connected和union中的複雜度都是find函式造成的,所以說它們的複雜度和find一樣。

find主要功能就是從某個節點向上遍歷到樹根,其時間複雜度就是樹的高度。我們可能習慣性地認為樹的高度就是logn,但這並不一定。logn的高度只存在於平衡二叉樹,對於一般的樹可能出現極端不平衡的情況,使得「樹」幾乎退化成「鍊錶」,樹的高度最壞情況下可能變成n。

三、平衡優化

1、小數合併到大樹

小一些的樹接到大一些的樹下面,這樣就能避免頭重腳輕,更平衡一些。解決方法是額外使用乙個size陣列,記錄每棵樹包含的節點數,我們不妨稱為「重量」:

class uf 

}/* 其他函式 */

}

比如說size[3] = 5表示,以節點3為根的那棵樹,總共有5個節點。這樣我們可以修改一下union方法:

public void union(int p, int q)  else 

count--;

}

分析:

通過比較樹的重量,就可以保證樹的生長相對平衡,樹的高度大致在logn這個數量級,極大提公升執行效率。

此時,find,union,connected的時間複雜度都下降為 o(logn),即便資料規模上億,所需時間也非常少。

2、路徑壓縮

什麼是路徑壓縮?簡單地說,就是壓縮樹的高度,使樹高始終保持在常數

這樣find就能以 o(1)的時間找到某一節點的根節點,相應的,connected和union複雜度都下降為 o(1)。

private int find(int x) 

return x;

}

分析:

呼叫find函式每次向樹根遍歷的同時,順手將樹高縮短了,最終所有樹高都不會超過 3(union的時候樹高可能達到 3)。

四、總結

class uf 

}public void union(int p, int q) else

count--;

}public boolean connected(int p, int q)

private int find(int x)

return x;

}}

建構函式初始化資料結構需要 o(n) 的時間和空間複雜度;連通兩個節點union、判斷兩個節點的連通性connected、計算連通分量count所需的時間複雜度均為 o(1)。

並查集詳解

其實並查集顧名思義就是有 合併集合 和 查詢集合 兩種操作的關於資料結構的一種演算法。並查集演算法不支援分割乙個集合。用集合中的某個元素來代表這個集合,該元素稱為集合的代表元。乙個集合內的所有元素組織成以代表元為根的樹形結構。對於每乙個元素 parent x 指向x在樹形結構上的父親節點。如果x是根...

詳解並查集

並查集主要用來求解不相交集合的問題,主要針對於合併和查詢兩種演算法。並查集的實現主要包含了三個部分 並查集的初始化是對單個資料建立了乙個單獨的集合,每個集合應該包含以下兩個資料 由以上兩個資料,一般的並查集的結構一般有兩種表示形式 結構體構造如下 define max 50 struct node ...

並查集詳解

看大佬的形象解釋 並查集 按我現在對這個的理解 就是給你一堆數,然後給你兩個兩個數的關係,然後關係的傳遞性 連帶性 這些數就都有了關係 有關係的數組成乙個陣列,然後輸出這個一維陣列,裡面有幾個沒關係的陣列 應該怎麼做呢?第一種解釋是 每給兩個數就把乙個數當成祖宗,把另乙個數當成孩子,然後給了孩子和另...