1.並查集的實現方法,通常通過一個 set[ i ] 數組實現,其中set[ i ] 表示元素 i 所在的集合。
例如:
i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
set[i] | 1 | 2 | 1 | 4 | 2 | 6 | 1 | 6 | 2 | 2 |
2.並查集的實現設計
普通情況下可以通過一個簡單的for循環在時間複雜度爲O(N)實現元素所在集合的合併,但是這種方法在數據比較大的情況下效率偏低。因此,通過爲了提高集合的合併那麼:
每個集合用一個“有根樹”表示,定義數組set[1....n],set[i] = i,則i表示本集合,而且是本集合對應的樹根。set[j]=j,j!=i,那麼j是i的父節點。
i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
set[i] | 1 | 2 | 3 | 2 | 1 | 3 | 4 | 3 | 3 | 4 |
int find(int x)
{
int r = x;
while( set[r] != r )
r = set[r];
return r;
}//時間複雜度最壞情況下O(N),一般情況下是O(lgN)
void merge(int a,int b)
{
if( a < b )
set[b] = a;
else
set[a] = b;
}//時間複雜度是O(1);
爲了進一步優化,那麼需要減小建樹的深度,因此需要對merge函數進行優化,從而是find函數的最壞情況複雜度爲O(lgN),優化如下,
void merge3(a,b)
{
if (height(a) == height(b))
{
height(a) = height(a) + 1;
set[b] = a;
}
else if (height(a) < height(b))
set[a] = b;
else
set[b] = a;
}//其中height可以通過使用結構體增設變量。
int find2(x)//帶路徑壓縮的查找算法。
{
int r = x;
while (set[r] <> r) //循環結束,則找到根節點
r = set[r];
int i = x;
while (i != r) //本循環修改查找路徑中所有節點
{
int j = set[i];
set[i] = r;
i = j;
}
}
以HDOJ1232 題目爲例,具體的實現細節代碼如下:
//Time:31MS
//Mem :212K
#include <stdio.h>
struct inode
{
int parent;
int height;
};
inode node[1002];
int ans;
int find(int x)
{
int r = x;
while(node[r].parent != r)
r = node[r].parent;
int i=x,j;
while(i != r)
{
j = node[i].parent;
node[i].parent = r;
i = j;
}
return r;
}
void merge(int x,int y)
{
int fx,fy;
fx = find(x);
fy = find(y);
if(fx == fy)
return;
else
{
if(node[fx].height == node[fy].height)
{
node[fx].parent = fy;
node[fy].height++;
}
else if(node[fx].height > node[fy].height)
node[fy].parent = fx;
else
node[fx].parent = fy;
}
}
int main()
{
int n,m,i,x,y;
while(scanf("%d",&n),n)
{
ans = 0;
for(i=1; i<=n; i++)
{
node[i].parent = i;
node[i].height = 1;
}
for(scanf("%d",&m);m>0; m--)
{
scanf("%d %d",&x,&y);
merge(x,y);
}
for(int i=1;i<=n;i++)
if(node[i].parent == i)
ans++;
printf("%d\n",ans-1);
}
return 0;
}
//莫名其妙帶查找合併優化的代碼AC的時間居然是一樣 - -!
應用:HDOJ 1272(小希的迷宮)http://acm.hdu.edu.cn/showproblem.php?pid=1272
解題思想:可以通過判斷輸入的迷宮,是否是一個連通圖。
if(輸入的節點在一個結合內){
if(節點數目 == 邊的數目-1)//是一棵樹,而不是圖,另外得特別注意,都爲0的情況,
yes
else
no
}
else
no
實現如下:
//Time:62MS
//Mem :1080K
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
#define MAX 100001
int visited[MAX];
int set[MAX];
int find(int x)
{
int r = x;
while(set[r]!=r)
r = set[r];
set[x] = r;
return r;
}
void merge(int a, int b)
{
int fa = find(a);
int fb = find(b);
set[fa] = fb;
}
int main()
{
int n,m;
while(true)
{
int node_num = 0;//初始化節點數目
int line_num = 0;//邊的數目
int set_num = 0;//節點的結合數目
for(int i=1;i<=100000;i++)
set[i] = i;
memset(visited, 0, sizeof(visited));
while(scanf("%d %d",&n,&m)!=EOF)
{
if((n==0 && m==0) || (n==-1&&m==-1))
break;
line_num++;
visited[n] = 1;
visited[m] = 1;
merge(n,m);
}
if(n==-1 && m==-1)
break;
for(int i=1;i<=100000;i++)
{
if(visited[i]==1 && set[i]==i)
set_num++;
if(visited[i] == 1)
node_num++;
}
if(node_num == 0)//特別注意0節點的情況,是yes
printf("Yes\n");
else if(set_num == 1 && line_num==node_num-1)
printf("Yes\n");
else
printf("No\n");
}
return 0;
}