1. 程式人生 > >並查集 入門 以及狀態壓縮 C

並查集 入門 以及狀態壓縮 C

並查集(Union-find Sets)是一種非常精巧而實用的資料結構,它主要用於處理一些不相交集合的合併問題。一些常見的用途有求連通子圖、求最小生成樹的 Kruskal 演算法和求最近公共祖先(Least Common Ancestors, LCA)等。

使用並查集時,首先會存在一組不相交的動態集合 S={S1,S2,,Sk}S={S1,S2,⋯,Sk},一般都會使用一個整數表示集合中的一個元素。、

顧名思義 並查集有兩個操作:

1:合兩個集合

2:找某元素屬於哪個集合 並查集的實現原理也比較簡單,就是使用樹來表示集合,樹的每個節點就表示集合中的一個元素,樹根對應的元素就是該集合的代表 如圖

              
                        並查集的表示                           初始化並查集  有{a,b,d,c}和{e,f,g}兩個集合其中a,和e分別為l兩棵數的根也是各個集合的代表,對於並查集這種資料結構查詢一個元素是否在集合裡面相對是比較快的它僅僅只需要不斷尋找其父節點一直到根節點複雜度為O(N)並且其實現方法也相對容易,只需用一個一維陣列來儲存其父指標(開始起指標指向他自己即A[N]=N)同樣合併兩個集合只需要將其中一個集合的根指標指向另一集合的根即可 查詢程式碼:
find2(x)
{
   r = x;
   while (set[r] != r)
      r = set[r];
   return r;
}
合併程式碼:
merge2(a, b)
{
    if (a<b)
       set[b] = a;
    else
       set[a] = b;
}


但是當一個集合形成的樹深度太深在遞迴的時候便失去啦它的意思還用可能當樹退化成一根鏈事其處理和陣列就差不多啦,對於這種問題通常有兩種解決方法 1:將深度小的集合合併在深度大的集合上去; 2:路徑壓縮; 路徑壓縮:路徑壓縮,就是在每次查詢時,令查詢路徑上的每個節點都直接指向根節點如圖
同樣路徑壓縮有兩個方法分別為遞迴版和非遞迴版 遞迴版:
int find(int x)       //查詢x元素所在的集合,回溯時壓縮路徑
{
    if (x != parent[x])
    {
        parent[x] = find(parent[x]);     //回溯時的壓縮路徑
    }         //從x結點搜尋到祖先結點所經過的結點都指向該祖先結點
    return parent[x];
}
非遞迴版:
int find(int x)
{
    int k, j, r;
    r = x;
    while(r != parent[r])     //查詢跟節點
        r = parent[r];      //找到跟節點,用r記錄下
    k = x;        
    while(k != r)             //非遞迴路徑壓縮操作
    {
        j = parent[k];         //用j暫存parent[k]的父節點
        parent[k] = r;        //parent[x]指向跟節點
        k = j;                    //k移到父節點
    }
    return r;         //返回根節點的值            
}
圖片等資料來自於:作者:CYJB
出處:http://www.cnblogs.com/cyjb/
GitHub:https://github.com/CYJB/