並查集詳解


  最近回顧了一下最小生成樹,發現以前學的太淺了。。。沒有仔細分析其中的每一步,汗顏。。發現Kruskal算法可以用到並查集的東西,就把並查集重新溫習了一下。注:這里有部分內容來自網絡,有部分內容在算法導論(第二版21章)中也有。

   並查集

      並查集是一種樹形結構,又叫“不相交集合”,保持了一組不相交的動態集合,每個集合通過一個代表來識別,代表即集合中的某個成員,通常選擇根做這個代表。

  三種主要操作:

Make_Set(x):

建立一個新的集合,其唯一成員就是x,因此這個集合的代表也是x,並查集要求各集合是不相交的,因此要求x沒有在其他集合中出現過。

Find_Set(x):

返回能代表x所在集合的節點,通常返回x所在集合的根節點。有遞歸和非遞歸兩種方法,下面會有講解。

Union(x, y):

將包含x,y的動態集合合並為一個新的集合。合並兩個集合的關鍵是找到兩個集合的根節點,如果兩個根節點相同則不用合並;如果不同,則需要合並。

                     

並查集的優化:

Union(x, y)時按秩合並:

合並時,如果兩個集合的秩相同,任選一個根做為父節點,並增加其秩。

秩不同時,讓較小秩的集合指向較大秩的集合,這時秩的大小不變。

秩和集合的數目是不一樣的,秩表示節點高度的一個商界;集合的數目表示集合中節點的總數。

Find_Set(x)路徑壓縮:

在Find_Set(x)中,是查找路徑上的每個節點都直接指向根節點,這樣下次再找根節點的時間復雜度會變成o(1)

 

主要代碼:

 

//建立一個新的集合,每一個子節點就是一個數,本身就是他的根節點
void Make_Set(int x)
{
    father[x] = x;
    R[x] = 0;
}

//通過遞歸向上查找根節點,回溯時改變當前節點的父節點,直接指向根節點。
int Find_Set(int x)
{
    if(x != father[x])
        father[x] = Find_set(father[x]);
    return father[x];

}

//將根節點設置為-1的非遞歸方法
int Find_Set2(int x)
{
    int y = x;
    while(y!= -1)
        y = father[y];
    return y;
}

//兩個集合的合並算法
void Union(int x, int y)
{
    int GrandX = Find_set(x);
    int GrandY = Find_set(y);

    if(GrandX == GrandY)
        return;
    if(R[GrandX] < R[GrandY])
        father[GrandX] = GrandY;
    else
    {
        if(R[GrandX] == R[GrandY])
            R[GrandX]++;
        father[GrandY] = GrandX;    
    }
}

本文學習借助了勇幸的博客,感謝,這里注明鏈接:http://www.ahathinking.com/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM