並查集操作的簡單實現
- 原理:定義一個數組s[i]來表示第i個元素屬於哪個集團,因此初始化時s[i] = i;即每個元素都還是分散的。對於可以合並的兩個元素x與y,查找到他們兩個所屬的集團,將其中一個合並到另一個即可;
- 代碼實現:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 10;
int s[maxn];
void init_set(int n)
{
for(int i=1; i<=n; i++)
s[i] = i;
}
int find_set(int x)//查詢x的集團編號
{
return s[x] == x?x:find_set(s[x]);
}
void union_set(int x, int y)
{
x = find_set(x);
y = find_set(y);
if(x != y) s[x] = s[y];
}
int main()
{
int n,m,x,y;
while(~scanf("%d %d",&n,&m))
{
init_set(n);//初始化
for(int i=0; i<m; i++)
{
scanf("%d %d",&x,&y);
union_set(x, y);//將x的集團與y的集團合並
}
int ans = 0;
for(int i=1; i<=n; i++)//統計有多少個集團
if(s[i] == i)
ans++;
printf("%d\n",ans);
}
return 0;
}
合並的優化
- 將兩個集團合並時,可以看做是兩個樹的合並,而高度較小的樹合並到較大的樹上可以使樹的高度不變。引入一個數組height[i]來表示樹的高度即可
- 代碼實現:
void init_set(int n)
{
for(int i=1; i<=n; i++)
{
s[i] = i;
height[i] = 0;
}
}
void union_set(int x, int y)
{
x = Find_set(x);
y = Find_set(y);
if(height[x] == height[y])
{
height[x]++;
s[y] = x;
}
else
{
if(height[x] > height[y])
s[y] = x;
else
s[x] = y;
}
}
查詢的優化
- 每次搜索的過程中,如果順便將i所屬的集團改為根節點,再次查找i的時候就可以直接返回結果了
- 代碼實現
//遞歸查詢,容易爆棧
int find_set(int x)
{
if(x != s[x])
s[x] = find_set(s[x]);
return s[x];
}
//非遞歸查詢
int Find_set(int x)
{
int r = x;
while(r != s[r]) r = s[r];
int i=x,j;
while(i != r)
{
j = s[i];
s[i] = r;
i = j;
}
return r;
}