一、Tarjan算法求LCA
二、Tarjan算法求強連通分量【棧里存點---------每個點都屬於一個強連通分量】
(着重理解一下第11行的else if語句:dfn[ i ]!=0 且 instk[i]==0的點一定已經是另一個強連通分量里面的點了,所以就不用考慮了,所以用else if(instk[ i ])來判斷是不是已經是別的強連通分量里的點了,如果不是,則是當前點的祖先,可以用來更新low[ u ],這也是強連通分量的更新low[ u ]的精髓。)
1 void Tarjan(int u) { 2 dfn[u] = low[u] = ++tim; 3 in_stk[u] = 1; 4 stk[tp++] = u; 5 for (int i = head[u]; i != -1; i = e[i].next) { 6 int v = e[i].to; 7 if (!dfn[v]) { 8 Tarjan(v); 9 if (low[v] < low[u])low[u] = low[v]; 10 } 11 else if (in_stk[v]) { 12 if (low[v] < low[u])low[u] = low[v];//這里使用dfn[v]和low[v]都可以 13 } 14 } 15 if (dfn[u] == low[u]) { 16 scc_cnt++; 17 do { 18 tp--; 19 int tt = stk[tp]; 20 col[tt] = scc_cnt; 21 in_stk[tt] = 0; 22 vc[scc_cnt].push_back(tt); 23 } while (stk[tp] != u); 24 } 25 }
三、Tarjan算法求雙連通分量【棧里存邊------每條邊都屬於一個點雙連通分量】
(由於是無向圖,這里需要注意的是不能重復訪問邊,用else if(dfn[ u ]>dfn[ v ]&&v!=fa)來防止重復訪問)
1 void Tarjan(int u, int fa) { 2 dfn[u] = low[u] = ++tim; 3 int child = 0; 4 for (int i = head[u]; i != -1; i = e[i].next) { 5 int v = e[i].to; 6 struct edge e1; 7 e1.fr = u; e1.to = v; 8 if (!dfn[v]) { 9 stk.push(e1); 10 child++; 11 Tarjan(v); 12 if (low[v] >= dfn[u]) { 13 iscut[u] = true; 14 bcc_cnt++; bcc[bcc_cnt].clear(); 15 do { 16 struct edge e2 = stk.top(); stk.pop(); 17 if (col[e2.fr]!=bcc_cnt) { 18 bcc[bcc_cnt].push_back(e2.fr); 19 col[e2.fr] = bcc_cnt; 20 } 21 if (col[e2.to]!=bcc_cnt) { 22 bcc[bcc_cnt].push_back(e2.to); 23 col[e2.to] = bcc_cnt; 24 } 25 } while (e2.fr != u || e2.to != v); 26 } 27 } 28 else if (dfn[v] < dfn[u] && v != fa) { 29 stk.push(e1); 30 low[u] = min(low[u], dfn[v]);//注意這里必須是dfn[v],而不能是low[v],因為low[v]很可能在另一個雙連通分量已經被更新,這個時候用會出錯 31 } 32 if (fa < 0 && chile == 1)iscut[u] = false; 33 } 34 }