連通性(connectivity)問題


1 本文參考

  • 《Algorithms In C》,Robert Sedgewick.

2 連通性問題描述

2.1 問題數學示例

假設有個整數對,p-q解釋為p與q連通。如圖
c1
如果新輸入的對,可以由以前的輸入對連通,就不會輸出;如果不能由以前的對連通,就輸出這個對。例如2-9不在輸出之列,因為前面的對存在2-3-4-9的連通。

2.2 應用示例

  • 整數代表網絡節點,對代表網絡連通,因此網絡可以判斷p和q之間是否應經連通。
  • 電網
  • 更甚至與程序中定義的兩個等價變量

3 算法實現

首先假設連通的每個節點都存在一個數組中a[N],沒次都選擇兩個節點,判斷兩個節點是不是連通。

3.1 快速查找(quick-find)算法

程序中當且僅當p與q連通的時候,id[p]與id[q]相等。

 1:  #include <stdio.h>
 2:  #define N 10
 3:  int main(int argc, char *argv[])
 4:  {
 5:      int count;
 6:      int i=0,p,q,t,id[N];
 7:      for (i=0; i < N; ++i)
 8:      {
 9:          id[i]=i;
10:      }
11:     while(scanf("%d %d",&p,&q)==2)
12:      {
13:          if(id[p]==id[q]) continue;
14:          count=0;
15:          for(t=id[p],i=0;i<N;++i)
16:          {
17:              if (t==id[i])
18:              {
19:                  count++;
20:                  id[i]=id[q];
21:              }
22:          }
23:          printf("\n%d-%d\n",p,q);
24:          for (i=0; i < N; ++i)
25:          {
26:              printf("%d ",id[i]);
27:          }
28:      }
29:      return 0;
30:  }

3.2 快速並集算法

相比上面的算法,並集運算計算量少,查找運算計算量大,算是算法的改進。根本就是:每個節點都沿着樹上移,找到各自的根節點(root)。

 1:  #include <stdio.h>
 2:  #define N 10
 3:  int main(int argc, char *argv[])
 4:  {
 5:      int i,j;
 6:      int p,q;                    /* 輸入的連通對 */
 7:      int id[N];
 8:      for (i = 0; i < N; ++i)
 9:      {
10:          id[i]=i;
11:      }
12:      while(scanf("%d %d",&p,&q)==2)
13:      {
14:          for(i=p;i!=id[i];i=id[i]);
15:          for(j=q;j!=id[j];j=id[j]);
16:          if(i==j) continue;
17:          id[i]=j;
18:          printf("\n%d-%d\n",p,q);
19:          for (i=0; i < N; ++i)
20:          {
21:              printf("%d ",id[i]);
22:          }
23:      }
24:      printf("\n");
25:      return 0;
26:  }

結果圖解如下:
bingshu bing

3.3 快速並集的加權算法

上面的算法,我們並不能保證每一種情況,它的速度都比快速查找有實質性的提高。這個是修改版,它使用一個額外的數組sz完成維護的目的,為每個對象用id[i]==i來表示,這樣可以組織樹的增長。下圖描述了快速並集加權算法,連接兩棵樹的時候,較小的數的根要附屬到較大的數的根下面。這樣節點與根的距離短,多以查找效率要高:
jiaquan

3.4 對分路徑壓縮-快速並集的加權算法(部分)

這個比全路徑壓縮算法簡單:然每個節點在連通的過程中,跳到上一級樹,指向上一級的節點,從而實現路徑壓縮。與上個算法相比,改進:

for(i=p;i!=id[i];i=id[i])
    id[i]=id[id[i]];
for(j=q;j!=id[j];j=id[j])
    id[j]=id[id[j]];

yasuo
如圖,當處理1和6的時候,讓1、5、6都指向3,得到的樹比上面的算法更扁平。

3.5 全路徑壓縮-快速並集的加權算法

 1:  #include <stdio.h>
 2:  #include <stdlib.h>
 3:  #define BUFFER_MAX_SIZE 16
 4:  int main(int argc, char *argv[]) {
 5:      size_t con[BUFFER_MAX_SIZE];
 6:      size_t con_cnt[BUFFER_MAX_SIZE];
 7:      size_t elem[BUFFER_MAX_SIZE][BUFFER_MAX_SIZE];
 8:      size_t elem_cnt[BUFFER_MAX_SIZE];
 9:      size_t p, q, r, i, j, k;
10:      for(r=0; r<BUFFER_MAX_SIZE; r++) {
11:          con[r]=r;
12:          con_cnt[r]=1;
13:          elem[r][0]=r;
14:          elem_cnt[r]=1;
15:      }
16:  
17:      while(scanf("%d-%d", &p, &q)==2) {
18:          if(p>=BUFFER_MAX_SIZE||q>=BUFFER_MAX_SIZE)
19:              exit(EXIT_FAILURE);
20:          for(i=p; i!=con[i]; i=con[i]);
21:          for(j=q; j!=con[j]; j=con[j]);
22:          if(i==j)
23:              continue;
24:          if(con_cnt[i]<con_cnt[j]) {
25:              k=elem_cnt[j];
26:              for(r=0; r<elem_cnt[i]; r++)
27:                  elem[j][elem_cnt[j]++]=elem[i][r];
28:              for(r=k; r<elem_cnt[j]; r++)
29:                  con[elem[j][r]]=j;
30:              con_cnt[j]+=con_cnt[i];
31:          }
32:          else {
33:              k=elem_cnt[i];
34:              for(r=0; r<elem_cnt[j]; r++)
35:                  elem[i][elem_cnt[i]++]=elem[j][r];
36:              for(r=k; r<elem_cnt[i]; r++)
37:                  con[elem[i][r]]=i;
38:              con_cnt[i]+=con_cnt[j];
39:          }
40:          printf("%d-%d\n", p, q);
41:          for(r=0; r<BUFFER_MAX_SIZE; r++)
42:              printf("%d\t", con[r]);
43:          printf("\n");
44:      }
45:      exit(EXIT_SUCCESS);
46:  }

Date: 2012-06-18 15:10:59

Author: Crowning

Org version 7.8.11 with Emacs version 23

Validate XHTML 1.0


免責聲明!

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



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