並查集是一種維護集合的數據結構,它的名字中“並”“查”“集”分別取自Union(合併)、Find(查找)、Set(集合)這3個單詞。也就是說,並查集支持下面兩個操作:
①合併:合併兩個集合。
②查找:判斷兩個元素是否在一個集合。
那麼並查集是用什麼實現的呢?
其實就是用一個數組:
int father[N];
其中fahter[i]表示元素i的父親結點,而父親結點本身也是這個集合內的元素(1≤i≤N)。例如father[1]=2就表示元素1的父親結點是元素2,以這種父系關係來表示元素所屬的集合。另外,如果father[i]=i,則說明元素i是該集合的根結點,但對同一個集合來說只存在一個根結點,且將其作爲所屬集合的標識。
#include<iostream>
using namespace std;
const int max_union_find_set = 1001;
int father[max_union_find_set] = { 0 };
//初始化
void init(int father[]) {
for (int i = 1;i <= max_union_find_set;i++)
{
father[i] = i;
}
}
//查找(遞歸)
int find_father(int x) {
if (father[x] == x)
{
return x;
}
else
{
return find_father(father[x]);
}
}
//合併
void union_father(int a, int b) {
int father_a = find_father(a);
int father_b = find_father(b);
if (father_a != father_b)//如果不在一個集合,則合併
{
father[father_a] = father_b;
}
}
int main() {
system("pause");
return 0;
}
在合併的過程中,只對兩個不同的集合進行合併,如果兩個元素在相同的集合中,那麼就不會對它們進行操作。這就保證了在同一個集合中一定不會產生環,即並查集產生的每一個集合都是一棵樹。
上面講解的並查集查找函數是沒有經過優化的,在極端情況下效率較低。現在來考慮一種情況,即題目給出的元素數量很多並且形成一條鏈,那麼這個查找函數的效率就會非常低。
下面給出解決方案:路徑優化
這相當於把查找結點的路徑上的所有結點的父親都指向根結點
則查找的複雜度可以變爲O(1)。
int find_father(int x) {
if (father[x] == x)
{
return x;
}
else
{
int root_val = find_father_2(father[x]);
father[x] = root_val;//將根結點賦值給father[x]
return root_val;//返回根結點
}
}