並差集的三大應用整理與總結

並查集的基本操作有三個:

  1. make(s):建立一個新的並查集,其中包含 s 個單元素集合。
  2. union(x, y):把元素 x 和元素 y 所在的集合合併,要求 x 和 y 所在的集合不相交,如果相交則不合並。
  3. find(x):找到元素 x 所在的集合的代表,該操作也可以用於判斷兩個元素是否位於同一個集合,只要將它們各自的代表比較一下就可以了。
1.make(s):
本人理解的make就是對並差集初始化:
/*make()*/
for(int i=1;i<=n;i++)f[i]=i;

2.union(x,y)
1)

按秩合併。該方法使用秩來表示樹高度的上界,在合併時,總是將具有較小秩的樹根指向具有較大秩的樹根。簡單的說,就是總是將比較矮的樹作爲子樹,添加到較高的樹中。爲了保存秩,需要額外使用一個與 uset 同長度的數組,並將所有元素都初始化爲 0。

void union(int x, int y)
{ 
    if ((x=find(x)) == (y=find(y))) return;
    if (rank[x]>rank[y])uset[y]=x; 
    else 
	{ 
        uset[x] = y; 
        if (rank[x]==rank[y])rank[y]++; 
    } 
} 



2)

按集合中包含的元素個數(或者說樹中的節點數)合併。將包含節點較少的樹根,指向包含節點較多的樹根。這個策略與按秩合併的策略類似,同樣可以提升並查集的運行速度,而且省去了額外的 rank 數組。

這樣的並查集具有一個略微不同的定義,即若 uset 的值是正數,則表示該元素的父節點(的索引);若是負數,則表示該元素是所在集合的代表(即樹根),而且值的相反數即爲集合中的元素個數。

void union(int x, int y) 
{ 
    if ((x=find(x))==(y=find(y)))return; 
    if (uset[x]<uset[y]) 
	{ 
        uset[x]+=uset[y]; 
        uset[y]=x; 
    } else { 
        uset[y]+=uset[x]; 
        uset[x]=y; 
    } 
} 

另,當然平時我們也能簡寫成這樣的形式:
void uinon(int x,int y)
{
	int a=find(x),b=find(y);
	if(a!=b)f[a]=b;
}

3.find(x)

這裏給出啓發式優化過後的代碼:

int find(int x)
{
	return x==f[x]?x:f[x]=find(f[x]);
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章