演算法學習 並查集

2021-08-16 03:22:03 字數 3005 閱讀 4442

大家肯定有聽說過社交網路裡面的六人理論吧,說是可以通過六個人的聯絡認識世界上的任意乙個人。比如我想認識一下機械系的系花,我先找到機械系的朋友,然後通過朋友介紹認識。這樣可以發現我們的社交圈子其實是有部分重疊的。當然也有可能是我的圈子太小,根本沒有什麼朋友認識那個圈子裡面的人,也有很大可能是機械系花404。我們之間是否存在聯絡,即連線問題,這類問題往往可以通過並查集實現。

並查集,顧名思義,我們可以對集合進行並和查兩種操作,即合併兩者之間的聯絡或者查詢兩者之間是否存在聯絡。

上面那個例子中人的姓名和關係我們就可以使用乙個陣列id來進行標記,索引i表示人名,id[i]則表示聯絡。一開始賦值的時候,我們先令id[i] = i;表示每乙個元素都和自己有關,後面需要建立聯絡時修改一方的id值,使它指向另乙個元素。最終通過查詢id,即可判斷是否存在聯絡。

可以看到我們的查詢**很簡短,它的速度也是很快的,時間複雜度為o(1)級別的。

namespace uf1

~unionfind()

int find(int p)

bool isconnected(int p, int q)

void unionelements(int p, int q)};}

雖然我們的查詢是很快的,但是我們的合併卻仍有可以優化的地方。

當我們合併兩個元素時,總是讓後乙個指向前乙個,但是這樣話,樹的高度將會越來越高,我們的查詢效率也會隨之降低。那我們有什麼辦法降低樹的高度呢?當然有,我們可以將元素直接連線到根節點處,就如下圖所示,直接將9連線到8處即可,這樣通過查詢根節點是否相同,我們就可以判斷兩者是否連線。

**如下:

我們不斷查詢父節點的父節點,直到parent[i] == i就表示找到了根節點。

class

unionfind

~unionfind()

//找到從屬的根節點

int find(int p)

bool isconnected(int p, int q)

void unionelements(int p, int q)

};

通過前面的**我們可以看到,我們總是將後乙個指向前面乙個,但是有時候讓前面乙個指向後面乙個反而樹的高度更小。其實這裡有兩種優化,一種是通過統計以i為根的元素個數sz,還有一種是統計以i為根的樹的高度rank。sz和rank大的優先作為根節點。

同時通過下面兩張我們也可以清晰地看出,兩者相比而言,rank更具合理性。

sz圖示:

rank圖示:

//找到從屬的根節點

int find(int p)

bool isconnected(int p, int q)

void unionelements(int p, int q)

else}};

rank的話僅僅是合併處有所不同:

void unionelements(int p, int q)

else

if(rank[qroot] < rank[proot])

else

}

很多時候評價乙個演算法是否優秀,我們不應該只看它一方面的表現,還得看它在很多特殊的情況下,效能是否還能保持穩定。在並查集的測試中,我們的測試都是隨機的,但是當我們的樣例是乙個單方面依賴的樹,那麼搜尋的效率就回下降很多。這時候,計算機科學家就發明了一種路徑壓縮的演算法。

路徑壓縮演算法採用兩步一跳的方式,同時在搜尋的過程中,將原有的結構進行修改壓縮。這裡大家可能會擔心會不會跳出根節點,這點不用擔心,因為根節點的父節點還是自己,所以並不會跳出。

路徑壓縮完成之後,當我們進行下一次操作時,樹的高度就大大降低了。

//找到從屬的根節點

int find(int p)

//// return p;

//跳過乙個進行搜尋根,同時改變結構

if(p != parent[p])

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

return parent[p];

}bool isconnected(int p, int q)

void unionelements(int p, int q)

else

if(rank[qroot] < rank[proot])

else}};

經過100000條隨機資料測試,我們得到了如下效能資料:

當然,由於隨機union的情況不同,uf4稍微比uf3慢了一點,這裡可以諒解。但是我們可以發現,理論上最快的uf5竟然明顯地慢了。經過分析我們可以發現,因為是uf5使用了遞迴消耗了一定的時間導致變慢。所以說,演算法的設計還得考慮實際的情況。我們需要考慮的方面還有很多。

演算法學習之並查集

並查集是一種樹型的結構,常常用來處理一些不相交的集合的合併和查詢問題。查詢 確定元素所在的集合。合併 將兩個集合合併成乙個集合。查詢v所在集合的根節點 int find int v 合併 void merge int x,int y 優化1 int find int x k x while k r ...

演算法學習之 並查集

並查集用於解決連線類問題,判斷網路中節點間的連線狀態。與路徑類問題相比,並查集只回答了節點之間是否連通,而具體的連通路徑並不能確定,因此並查集在某些場景下非常高效。如前所述,此處的並查集實現只提供兩個介面 是否連線,元素合併。下面 使用乙個陣列來記錄每個元素所對應的類別,如果兩個元素的類別相同,則稱...

演算法學習記錄 並查集

上大物學了會並查集,感覺挺簡單的,而且很好玩,繼dfs,bfs和floyd演算法外又學了一種求連通塊的演算法,綜合下來這幾種演算法各有優劣吧.並查集演算法詳解見此 模板如下 include using namespace std define maxn 100001 int father maxn ...