tarjan算法不是很懂先mark一下。
生活随笔
收集整理的這篇文章主要介紹了
tarjan算法不是很懂先mark一下。
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
?前面為轉載的。后面是自己的理解。
? ? ??強連通的tarjan 算法是基于圖的深度優先搜索(這個經常用于獲取圖的各種信息)。下面說一下幾個約定:?
1 、必要性。 ? ? ? ? ? ? ?如果 u,v屬于同一個強連通分支則必定存在一條 u到 v的路徑和一條v到u的路徑。合并兩條則有 u->v->v1->v2->..vn->u, 若頂點v1到vn都是v 的子孫,則有 vn->u這樣一條后向邊。 ? ? ? ? ? 如果v1到vn 不全是vn的子孫,則必定有一個是u的祖先,我們不妨設vi為u的祖先,則有一條后向邊 V[i-1] ->v[i]。
2.、充分性。 ? ?我們設 u1->u2->u3..->un->u->v->v1->v2..->vn,我們假設后向邊vn指向ui則有這樣一個環:u[i]->u[i+1]...->u->v->v1->v2..->v[n-1]->v[n]->u[i],易知,有一條u->v的路徑,同時有v->u的路徑。固u,v屬于同一連通分支。
? ? ? ? 在算法開始的時候,我們把i圧入棧中。根據low[i] 和 dfn[i]的定義我們知道, ? ? ? ? 如果low[i] < dfn[i] 則以i為頂點的子樹中,有指向祖先的后向邊,則說明i和i的父親為在同一連通分支,也就是說留在棧中的元素都是和父結點在同一連通分支的。 ? ? ? ? ?如果low[i] == dfn[i],則?i為頂點的子樹中沒有后向邊,那么由于 ?留在棧中的元素都是和父結點在同一連通分支的,我們可以知道,從棧頂到元素i構成了一個連通分支。顯然,low[i]不可能小于dfn[i]。 __________________________________________________________________________________________________----我的理解,感覺有點類似于并查集,一開始假設有n個連通分量既dfn,low是我們進行判斷后修正的既low[n]=m表示實際上n這點屬于m這個連通分量,個點屬于哪個連通分量。然后進行深搜當找到一個環既(Instack[j]==true)這次找到的點是我們之前搜索到的,那這個點就屬于dfn[j]這個連通分量。
三種tarjan算法(上)
。這篇算是做一個總結吧。- 求強連通分量
- 求無向圖的割和橋
- 最近公共祖先
求強連通分量
?基本概念:
? ? ? 強連通是有向圖才有的概念。一個有向圖是強連通的是對于每個有序對 u,v,存在一條從 u到v ?的路徑。一個有向圖的強連通分量是指它的極大連通子圖。就是你可以從 a地走到 b 地,同樣可以從b地走到a地,a,b就是連通的。 ? ? ? 下圖(圖片來自這里)中 頂點 {1,2,3,4}是一個強連通分量,因為它們兩兩都可以到達。直觀上來講,所謂的強連通,(如果有至少有一個頂點)就是可以至少形成一個環。如1->3->4->1就是一個環,它們就是強連通的。注意也有像{5},{6}這樣的的連通分量。? ? ??強連通的tarjan 算法是基于圖的深度優先搜索(這個經常用于獲取圖的各種信息)。下面說一下幾個約定:?
- 時間戳 ? ? ? :DFN[i]是指結點i被遍歷的時間。
- Low[i] ? ? ? :是指在搜索樹中,結點i和其子孫可以訪問到的最早的祖先,Low[i] = Min(DFN[i], DFN[j], Low[k])其中j是i的祖先(我們把子孫連到祖先的邊叫后向邊),k是i 的子女。
- 結點的顏色:color[i]是用于標示結點i的狀態:白色指還沒到搜索到,灰色正在被搜索,黑色處理完畢。在實際操作中用-1,0,1分別代表白色、灰色、黑色。
? ? ? tarjan算法的步驟:? ? ?
- 先把所有的結點的顏色都初始化白色,并把棧清空。
- 找到一個白色的結點i(即結點的顏色為白色)
- 給結點一個時間戳,把結點圧入棧中,并把結點標記為灰色。令Low[i] = DFN[i]?
- 遍歷結點i 的每條邊(i,j)。若color[j]是白色,就對結點i重復2~5步驟。并令Low[i] = min(Low[j],low[i]).如果color[j]是灰色,令Low[i] = min(Low[i],DFN[j])。如果是黑色不做任何處理。
- 把結點的顏色改為黑色,如果Low[i] = DFN[i],就把從棧頂到結點i間的元素彈出
- 重復步驟2,至到沒有白色頂點
? ? ? 下面是算法的一個模板:
[cpp]?view plaincopy- <pre?name="code"?class="cpp">#include?<math.h>??
- #include?<stdio.h>??
- #include?<string.h>??
- #include?<stdlib.h>??
- ??
- //從頂點0開始??
- //?要用的話要初始化:調用Adj.initial?和?tarjan.initial??
- //要解決問題用調用tarjan.solve??
- //對tarjan.initial要傳入的參數是圖邊集Adj,和頂點個數n??
- ??
- const?int?maxn?=?11000;??
- //頂點的規模??
- const?int?maxm?=?210000;??
- //邊的規模,如果是無向圖要記得乘以2??
- ??
- const?int?GRAY?=?0;??
- const?int?WHITE?=-1;??
- const?int?BLACK?=?1;??
- ??
- typedef?struct?Edge{??
- ????int?s;??
- ????int?e;??
- ????int?next;??
- }Edge;??
- ??
- typedef?struct?Adj{??
- ????int?edge_sum;??
- ????int?head[maxn];??
- ????Edge?edge[maxm];??
- ??
- ????void?initial(){???
- ????????edge_sum?=?0;??
- ????????memset(head,-1,sizeof(head));??
- ????}??
- ??
- ????void?add_edge(int?a,?int?b){??
- ????????edge[edge_sum].s?=?a;??
- ????????edge[edge_sum].e?=?b;??
- ????????edge[edge_sum].next?=?head[a];??
- ????????head[a]?=?edge_sum++;??
- ????}??
- }Adj;??
- ??
- typedef?struct?Tanjan{??
- ????int?n;??
- ????int?*head;??
- ????Adj?*adj;??
- ????Edge?*edge;??
- ??
- ????int?cnt;??
- ????int?top;??
- ????int?cur;??
- ??
- ????int?dfn[maxn];??
- ????int?low[maxn];??
- ????int?color[maxn];??
- ????int?stack[maxn];??
- ????int?belong[maxn];??
- ??
- ????void?initial(Adj?*_adj,int?_n){??
- ????????n?=?_n;??
- ????????adj?=?_adj;??
- ????????head?=?(*adj).head;??
- ????????edge?=?(*adj).edge;??
- ????}??
- ??
- ????void?solve(){??
- ????????memset(dfn,-1,sizeof(dfn));??
- ????????memset(color,WHITE,sizeof(color));??
- ??
- ????????top?=?cnt?=?cur?=?0;??
- ????????for(int?i?=?0;?i?<?n;?i++)??
- ????????????if(color[i]?==?WHITE)//找到一個白色的頂點,就開始處理??
- ????????????????tarjan(i);??
- ????}??
- ??
- ????inline?int?min(int?a,?int?b){??
- ????????if(a?<?b)?return?a;??
- ????????else?return?b;??
- ????}??
- ??
- ????void?tarjan(int?i){??
- ????????int?j?=?head[i];??
- ??
- ????????color[i]?=?GRAY;//標記為灰色??
- ????????stack[top++]?=?i;//把結點圧入棧頂??
- ??
- ????????dfn[i]?=?low[i]?=?++cur;//給結點一個時間戳,并給Low初始化??
- ??
- ????????while(j?!=?-1){??
- ????????????int?u?=?edge[j].e;??
- ????????????if????????(dfn[u]?==?WHITE){??
- ????????????????tarjan(u);??
- ????????????????low[i]?=?min(low[i],low[u]);??
- ????????????//更新low???
- ????????????}else??if?(color[u]?==?GRAY)??
- ????????????????low[i]?=?min(low[i],dfn[u]);??
- ????????????//一條后向邊??
- ????????????j?=?edge[j].next;??
- ????????}??
- ??
- ????????color[i]?=?BLACK;??
- ????????if(low[i]?==?dfn[i]){??
- ????????????do{??
- ????????????????j?=?stack[--top];??
- ????????????????belong[j]?=?cnt;??
- ????????????}while(i?!=?j);??
- ????????????++cnt;????
- ????????}??
- ????}??
- }Tarjan;??
- ??
- Adj?adj;??
- Tarjan?tj;</pre><br>??
- <br>??
- <pre></pre>??
- <pre></pre>??
- <pre></pre>??
- <pre></pre>??
- <pre></pre>??
tarjan算法的簡單證明:
? ? ? ? ?首先,這邊再重復一下什么是后向邊:就是在深度優先搜索中,子孫指向祖先的邊。在一棵深度優先搜索樹中,對于結點v, 和其父親結點u而言,u,v 屬于同一個強連通分支的充分必要條件是 ?以v為根的子樹中,有一條后向邊指向u或者u的祖先。1 、必要性。 ? ? ? ? ? ? ?如果 u,v屬于同一個強連通分支則必定存在一條 u到 v的路徑和一條v到u的路徑。合并兩條則有 u->v->v1->v2->..vn->u, 若頂點v1到vn都是v 的子孫,則有 vn->u這樣一條后向邊。 ? ? ? ? ? 如果v1到vn 不全是vn的子孫,則必定有一個是u的祖先,我們不妨設vi為u的祖先,則有一條后向邊 V[i-1] ->v[i]。
2.、充分性。 ? ?我們設 u1->u2->u3..->un->u->v->v1->v2..->vn,我們假設后向邊vn指向ui則有這樣一個環:u[i]->u[i+1]...->u->v->v1->v2..->v[n-1]->v[n]->u[i],易知,有一條u->v的路徑,同時有v->u的路徑。固u,v屬于同一連通分支。
? ? ? ? 在算法開始的時候,我們把i圧入棧中。根據low[i] 和 dfn[i]的定義我們知道, ? ? ? ? 如果low[i] < dfn[i] 則以i為頂點的子樹中,有指向祖先的后向邊,則說明i和i的父親為在同一連通分支,也就是說留在棧中的元素都是和父結點在同一連通分支的。 ? ? ? ? ?如果low[i] == dfn[i],則?i為頂點的子樹中沒有后向邊,那么由于 ?留在棧中的元素都是和父結點在同一連通分支的,我們可以知道,從棧頂到元素i構成了一個連通分支。顯然,low[i]不可能小于dfn[i]。 __________________________________________________________________________________________________----我的理解,感覺有點類似于并查集,一開始假設有n個連通分量既dfn,low是我們進行判斷后修正的既low[n]=m表示實際上n這點屬于m這個連通分量,個點屬于哪個連通分量。然后進行深搜當找到一個環既(Instack[j]==true)這次找到的點是我們之前搜索到的,那這個點就屬于dfn[j]這個連通分量。
#define M 5010//題目中可能的最大點數
int STACK[M],top=0;//Tarjan算法中的棧
bool InStack[M];//檢查是否在棧中
int DFN[M];//深度優先搜索訪問次序int Low[M];//能追溯到的最早的次序
int ComponentNumber=0;//有向圖強連通分量個數
int Index=0;//索引號
vector<int> Edge[M];//鄰接表表示
vector<int> Component[M];//獲得強連通分量結果
int InComponent[M];//記錄每個點在第幾號強連通分量里
int ComponentDegree[M];//記錄每個強連通分量的度void Tarjan(int i)
{int j;DFN[i]=Low[i]=Index++;InStack[i]=true;STACK[++top]=i;for (int e=0;e<Edge[i].size();e++){j=Edge[i][e];if (DFN[j]==-1){Tarjan(j);Low[i]=min(Low[i],Low[j]);}elseif (InStack[j]) Low[i]=min(Low[i],DFN[j]);}if (DFN[i]==Low[i]){ComponentNumber++;do{j=STACK[top--];InStack[j]=false;Component[ComponentNumber].push_back(j);InComponent[j]=ComponentNumber;}while (j!=i);}
}總結
以上是生活随笔為你收集整理的tarjan算法不是很懂先mark一下。的全部內容,希望文章能夠幫你解決所遇到的問題。