詳解 並查集高階技巧 加權並查集 擴充套件域並查集

2022-08-21 21:36:13 字數 3147 閱讀 4216

可以理解為使用陣列實現的樹形結構,只儲存了每個節點的父節點(前驅)。

功能為:合併兩個節點(及其所在集合) 、 查詢節點所屬集合的代表節點(可以理解為根節點)。

以6個元素為例(編號0到5):把0單獨劃分為乙個集合;把1,2,3,4劃分為乙個集合;把5單獨劃分為乙個集合。

n個元素的並查集,只需要乙個容量為n的陣列f[n],值全部初始化為自己即可:for(int i=0;i主要**:find(x):  if(x == f[x]) return x;

return find(f[x]);

但若只是簡單的這樣做,會出現上圖第三個圓中的情況,即查詢某個節點時遞迴太多次。因此需要「路徑壓縮」,只需增加一步:

find(x):  if(x == f[x]) return x;

return f[x] = find(f[x]);

union(x,y):  int fx=find(x), fy=find(y);

if(fx != fy) f [fx] = fy;  // 此處換為f [fy] = fx也行,道理相同,意義和效果其實也一樣。

注意:一定是f [fx] = fy,而不是f [x] = y。只有把x和y的最終父節點(前驅)連線起來,所屬的兩個集合才算真正完全連通,整個邏輯也才能正確。

n個點有m對關係,把n個節點放入兩個集合裡,要求每對存在關係的兩個節點不能放在同乙個集合。問能否成功完成?

把每個節點擴充套件為兩個節點(一正一反),若a與b不能在一起(在同乙個集合),則把a的正節點與b的反節點放一起,把b的正節點與a的反節點放一起,這樣就解決了a與b的衝突。若發現a的正節點與b的正節點已經在一起,那麼說明之前的某對關係與(a,b)這對關係衝突,不可能同時滿足,即不能成功完成整個操作。

n個點,每個點擴充套件為兩個點(一正一反),則需要乙個容量為2*n的陣列f[n],值全部初始化為自己即可:for(int i=0;i<2*n;i++) f[i]=i;

(注意初始編號,若編號為[1,n],則初始化應該為:for(int i=1;i<=2*n;i++) f[i]=i;)

乙個點x的正點編號為x,反點編號為x+n(這樣每個點的反點都是+n,規範、可讀性強、不重複、易於理解)。

1)初始化2*n個節點的初始父節點,即它本身。

2)遍歷m對關係,對每對(a,b),先找到a和b的父節點,若相等則說明(a,b)的關係與之前的關係有衝突,不能同時解決,則得到結果:不能完成整個操作。

否則執行:union(a, b+n), union(b, a+n).  (這時已經find過了,直接執行f [fx] = fy這一句就等效與union(x, y) )

3)若m對關係都成功解決,則得到結果:能夠完成整個操作。

由於擴充套件域會直接使陣列容量翻倍,所有一般只解決這種「二分」問題,只擴充套件為2倍即可。

優點在於:結構簡單,並查集的操作也不需要做改變,非常易於理解。  缺點顯然就是:需要額外儲存空間。

n個節點有m對關係(m條邊),每對關係(每條邊)都有乙個權值w,可以表示距離或劃分成多個集合時的集合編號,問題依然是判斷是否有衝突或者有多少條邊是假的(衝突)等。

給n個節點虛擬乙個公共的根節點,增加乙個陣列s[n]記錄每個節點到虛擬根節點的距離,把x,y直接的權值w看為(x,y)的相對距離。

union(x,y,w)時額外把x,y到虛擬根節點的距離(s值)的相對差值設定為w;find(x)時,壓縮路徑的同時把當前s值加上之前父節點的s值,得到真實距離。

f[n]陣列記錄節點的父節點,s[n]陣列記錄節點到虛擬根節點的距離:  for(int i=0;iif(x==f[x])return x;

int t  = f[x];

f[x] = find(f[x]);

s[x] += s[t];

// s[[x] %= mod;  若s[x]表示劃分成mod個集合時的集合編號等情況時,則需要求餘。

return f[x];

int fx = find(x), fy = find(y);  //此時已經s[x]和s[y]都已經計算為真值。

if(fx != fy) e[maxm];

bool

cmp(edge a, edge b)

int f[2 *maxn];

int find(int

x) int

main()

if (i == m)printf("0"

);

else printf("%d"

, e[i].c);

return0;

}擴充套件域的並查集解法

#include #include 

#include

using

namespace

std;

typedef

long

long

ll;const

int maxn = 200000 + 10

;const

int gxs = 2; //

模2就只有0,1兩個值,分別代表兩個不同的集合

const

int mod = 2

;int

n, m;

int f[maxn], s[maxn]; //

f記錄父節點(前驅),s記錄到虛擬root點的距離

void

init()

//查詢

int finds(int

x) //

新建關係

void unions(int x, int y, int

w) }

struct

node

};vector

que;

intmain() );

}sort(que.begin(), que.end());

//從大到小排序

init();

for (int i = 0; i < m; i++)

//否則說明解決之前的衝突後,當前衝突也被解決。

}

else

}cout

<< 0

<

return0;

}

加權並查集解法

並查集 並查集

本文參考了 挑戰程式設計競賽 和jennica的github題解 陣列版 int parent max n int rank max n void init int n int find int x else void union int x,int y else 結構體版 struct node ...

並查集高階

n 個孤立點,m 次加邊操作,設 l i,j 表示結點 i 和 j 最早在第 l i,j 次操作後連通。在 m 次操作後,求出 sum n sum n l i,j 的值。解法 quad 並查集的時候記錄子樹大小即可。n 個孤立點,m 次加邊操作,q 次詢問,每一次問 u 和 v 最早在第幾次操作後連...

並查集 帶權並查集詳解

contents 一.引入 二.查詢函式與集合合併函式 偽 三.權值如何更新 從圖的角度理解 並查集實際上是一顆由若干個樹構成的森林,而如果還需要儲存邊的權值,則需要額外用乙個陣列d記錄每個結點和其父節點的權值 從集合的角度理解 並查集儲存的資訊是某點屬於哪個集合,而帶權並查集還能記錄某點和其父結點...