1. 程式人生 > 其它 >No.7.2 並查集演算法

No.7.2 並查集演算法

暫時沒看懂,只做記錄,後續再究!

一、求有幾個強盜同夥?

現在有10個強盜:

  1與2是同夥;  3與4是同夥;

  5與2是同夥;  4與6是同夥;

  2與6是同夥;  8與7是同夥;

  9與7是同夥;  1與6是同夥;

  2與4是同夥;

  強盜同夥的同夥,也是同夥。請問共有幾個獨立的強盜同夥?

解題思路:

  1.首先假設10個強盜各不相屬,用一維陣列表示 f[i] = i

  2.合併同夥:如果兩人是同夥,以靠左原則為準,比如:“5與2是同夥,左邊是5,那麼2從屬於5” <=>

    1). u,v ( f[u]=u, f[v]=v ), 靠左原則 f[v]=u;

    2). w,v( f[w]=w, f[v]=u ), "擒賊先擒王"原則,u,v 都要從屬於 w, 即 f[v]=w; f[u]=w;

    3). v, x ( f[v]=w, f[x]=x ), x 從屬於 v,f[x] = f[v] = w;

  上述過程即並查集演算法:每個點開始都是隻有一個節點的樹,通過一些條件合併成一顆大樹,合併過程要遵循靠左原則和擒賊先擒王原則,找到相同的父節點和根節點。

  

二、code

int t[100]={0},n,m;

//初始化,t[i]=i,表示每個點都是自己的父節點,相互之間沒有關係
void init(){
  int i;
  for(i=1;i<=n;i++){
    t[i]=i;
  }
}

int getf(int v){  // 尋找根節點
  if(t[v] == v)
    return v;
  else{
    t[v]=getf(t[v]);  // v 是一維陣列的索引座標,t[v] 才是其父節點,所以 t[v] = getf( t[v] ); 在遞迴過程中,中間節點的父節點也被更新
    return t[v];    // 返回值是父節點,不是 v
  }
}

/* 其實這個函式用一句就可以代替:t[ getf(y) ] = t[ getf(x) ];

void merge(int x,int y){

  int t1,t2;
  t1=getf(x);
  t2=getf(y);
  if(t1!=t2){
    t[t2]=t1;
  }
}

*/

int main(){
  int i,a,b;
  int num=0;
  scanf("%d %d",&n,&m);
  init();

  for(i=1;i<=m;i++){
    scanf("%d %d",&a,&b);
    t[ getf(b) ] = t[ getf(a) ];
  }

  for(i=1;i<=n;i++)
    printf("%d ",t[i]);

  for(i=1;i<=n;i++){
    if(t[i]==i)  // 在假設的前提下,只有根節點的值不變,可以求出群的個數
      num++;
  }
  printf("\n%d\n",num);
  getchar();getchar();return 0;
}

三、程式碼不大,演算法也不難,關鍵是怎麼分解、實現。這是才是入門的基礎!