並查集的優化---路徑壓縮與啟發式合併
阿新 • • 發佈:2019-01-29
並查集的優化分為兩類:一種是 優化查詢的路徑壓縮,一種是啟發式合併(按集合大小合併與按秩(高)合併)
路徑壓縮
a.描述:如果查詢的總路徑過長,尤其是一條鏈的情況下,那麼樸素的查詢可能會超時。於是,每次在查詢完根節點後,可以將查詢路徑上的節點直接指向根節點。
b.缺點:在一定程度上破壞了樹的結構,也不便於記錄其他的附加資訊,如有特殊要求,請慎用(除非能夠轉化為與根節點的關係)
下面是遞迴版的程式碼
int find(int x) //查詢x的祖先節點
{ // fa[x]表示x的父親節點
return fa[x]=(fa[x]!=x?find(fa[x]):x);
} //在返回的過程,當前x的節點也順便指向了根節點
啟發式合併
- 按秩(高)合併
描述:使用秩來表示樹高度的上界,在合併時,總是將具有較小秩的樹根指向具有較大秩的樹根,這樣找祖先會減少遞迴迭代的次數,最壞只有logN次。
注意:由於需要記錄rank[]來表示高度,所以一般不與路徑壓縮同用(會破壞數的高度)
void join(int x,int y)
{
int xx=find(x),yy=find(y); //xx,yy 分別表示x與y的祖先節點
if(xx==yy) return ;
if(rank[xx]>rank[yy]) //rank[] 表示樹的高度
fa[yy]=xx; //將yy接到xx上去
//由於xx的高度比yy的大,所以無需更新高度 rank[xx]>=rank[yy]+1
else
{
fa[xx]=yy;//將xx接到yy上去
if(rank[xx]==rank[yy])
rank[yy]++;
}
}
- 按集合大小合併(啟發式合併)
描述:與按秩合併類似,只是將合併的參照改為集合的大小
優點:可與路徑壓縮同用
均攤複雜度:
void join(int x,int y)
{
int xx=find(x),yy=find(y);
if(xx==yy) return ;
if(size[xx]>size[yy]) //記錄size[]集合大小
{
size[xx]+=size[yy];
fa[yy]=xx;
}
else
{
size[yy]+=size[xx];
fa[xx]=yy;
}
}
PS:只有同時採用”路徑壓縮”與”啟發式合併”時,每次find的時間複雜度才能降為
以上就是並查集中查與並的優化,至於優化的效率如何,可自行找試題測試