初談這個話題相信每一位都會感到一絲疑惑,主要原因是這個詞中“分量”一詞,當然,如果僅是為了了解和使用這兩個術語,就不必在意這個無關大體的詞語。
好了,該談談正題了,所謂雙連通與強連通,最大的差別,也是最本質的差別就是前者適用於無向圖中,而后者適用於有向圖。至於兩者的概念是一樣的,就是圖中有a點、b點,從a點可到達b點,同時從b點可到達a點。(若是有向圖必須延方向到達。)
其中雙連通分量可細分為:點-雙連通分量,邊-雙連通分量。所謂點-雙連通分量是指在一個無向圖中兩點間至少有兩條路徑,且路徑中(不算頭尾)的點不同。不同的點-雙連通分量最多有一個公共點,這個點必定是“割頂”。提到割頂不得不在這里啰嗦一下,割頂(如下圖)就是當刪去這個點時,連通塊的數量會增加。至於什么叫連通塊,可以理解為一個點的集合,若兩點間可直接或間接的連接則兩點在同一連通塊中。
至於邊-雙連通分量是指在一個無向圖中兩點間至少有兩條路徑,且路徑中的邊不同。邊-雙連通分量中一定沒有橋。而橋(如上圖)是指當刪去這個邊時,連通塊的數量會增加。
知識性的東西已經科普完了,下面大致說一下程序。
判斷無向連通圖是否連通:
void dfs(int v) { node_pointer w; visited[v] = TRUE; for(w = graph[v]; w; w = w->link) { if(!visited[w->vertex]) { dfs(w->vertex); } } } void connect() { for(int i = 0; i < n; i++) { if(!visited[i]) { dfs(i); } } }
求點-雙連通圖:
stack<int> s; int num=1,time=0; int id[1000]={0}; void tarjan(int x, int fa) { dfn[x]=low[x]=time++; for(int e=first[x];e!=-1;e=next[e]) { if(x!=fa&&dfn[x]<dfn[v[e]]) { s.push(e); if(dfn[x]==0) { tarjan(v[e], x); if(low[v[e]]<low[x]) low[x]=low[v[e]]; if(low[v[e]]>=dfn[x]) { int edge; do { s.pop(); edge=s.top(); id[u[edge]]=id[v[edge]]=num++; }while(u[edge]!=x||v[edge]!=v[e]); } } else if(dfn[v[e]]<low[x]) low[x]=dfn[v[e]]; } } }
求邊-雙連通圖:
void(int u,int fa) { dfn[u]=low[u]=++time; s[top++]=u; for(int e=first[u];e!=-1;e=next[e]) { if(v[e]!=fa) { if(!dfn[v[e]]) { tarjan(v[e],u); if(low[v[e]]<low[u]) low[u]=low[v[e]]; else if(low[v[e]]>dfn[u]) { for(s[top]=-1;s[top]!=v[e];) { id[s[--top]]=num; num++; } } } else if(dfn[v[e]]<low[u]) low[u]=dfn[v[e]]; } } }
求強連通圖:
void tarjan(int i) { int j; DFN[i]=LOW[i]=++Dindex; instack[i]=true; Stap[++Stop]=i; for (edge *e=V[i];e;e=e->next) { j=e->t; if (!DFN[j]) { tarjan(j); if (LOW[j]<LOW[i]) LOW[i]=LOW[j]; } else if (instack[j] && DFN[j]<LOW[i]) LOW[i]=DFN[j]; } if (DFN[i]==LOW[i]) { Bcnt++; do { j=Stap[Stop--]; instack[j]=false; Belong[j]=Bcnt; }while (j!=i); } } void solve() { Stop=Bcnt=Dindex=0; memset(DFN,0,sizeof(DFN)); for (int i=1;i<=N;i++) { if (!DFN[i]) tarjan(i); } }
感謝各位觀看我的博客,如有不足歡迎提出,同時希望你們能有所收獲,謝謝。