Tarjan算法 (强联通分量 割点 割边)
變量解釋:
low 指當(dāng)前節(jié)點(diǎn)在同一強(qiáng)連通分量(或環(huán))能回溯到的dfn最小的節(jié)點(diǎn)
dfn 指當(dāng)前節(jié)點(diǎn)是第幾個(gè)被搜到的節(jié)點(diǎn)(時(shí)間戳)
sta 棧
vis 是否在棧中
ans 指強(qiáng)連通分量的數(shù)量
top 棧頂
1.求強(qiáng)連通分量
定義:如果兩個(gè)頂點(diǎn)可以相互通達(dá),則稱兩個(gè)頂點(diǎn)強(qiáng)連通(strongly connected)。如果有向圖G的每?jī)蓚€(gè)頂點(diǎn)都強(qiáng)連通,稱G是一個(gè)強(qiáng)連通圖。有向圖的極大強(qiáng)連通子圖,稱為強(qiáng)連通分量(strongly connected components)。
算法:在有向圖中從一點(diǎn)(u)開始dfs,記錄dfn,搜到一個(gè)已在棧中的點(diǎn)(v)時(shí)用dfn[v] (low[v]也行,但只有求強(qiáng)連通分量時(shí)可以別的只能用dfn[v]) 嘗試更新low[u],并在回溯時(shí)更新沿路的點(diǎn)的low值,走到low值與dfn相同的點(diǎn)時(shí)記錄這個(gè)強(qiáng)連通分量即可。
也就是說:在同一個(gè)強(qiáng)連通分量中所有點(diǎn)low值相同,也就是有一個(gè)代表點(diǎn)(代表點(diǎn)即所有點(diǎn)的low值即強(qiáng)連通分量中dfn值最小的點(diǎn))
時(shí)間復(fù)雜度為O(E+V)
code
void tarjan(int u){dfn[u]=low[u]=++cnt;//初始化一點(diǎn)的dfn和lowsta[++top]=u,vis[u]=true;//入棧for(int i=head[u];i;i=edge[i].next){//鄰接表int v=edge[i].to;if(!dfn[v]){//如果沒走過tarjan(v);low[u]=min(low[u],low[v]);//回溯過程時(shí)low值傳遞}else if(vis[v]) low[u]=min(low[u],dfn[v]); //low[v]也行 用代表點(diǎn)更新}if(dfn[u]==low[u]) {//如果是代表點(diǎn) 記錄并出棧ans++;//記錄強(qiáng)連通分量個(gè)數(shù)while(sta[top]!=u){vis[sta[top]]=false;top--;}vis[sta[top]]=false;top--;}return ; }2.求無向圖的割點(diǎn)與割邊
割點(diǎn):在無向圖中,如果將一個(gè)點(diǎn)以及所有連接該點(diǎn)的邊都去掉,圖就不再連通,那么這個(gè)點(diǎn)就叫做這個(gè)圖的一個(gè)割點(diǎn)。
割邊:在無向圖中,如果將一條邊去掉,圖就不再連通則稱這條邊為圖的一個(gè)割邊。
求割點(diǎn):如果一個(gè)點(diǎn)(u)所連接的幾個(gè)節(jié)點(diǎn)(v)的low值大或等于此節(jié)點(diǎn)(u)的dfn值時(shí)說明之后的節(jié)點(diǎn)(v)無法連接到比此點(diǎn)(u)更早的點(diǎn)上,則說明這個(gè)節(jié)點(diǎn)(u)是一個(gè)割點(diǎn)。PS:根節(jié)點(diǎn)需特判,當(dāng)根節(jié)點(diǎn)在dfs樹有兩個(gè)或更多個(gè)子樹時(shí)則說明根節(jié)點(diǎn)是割點(diǎn)
求割邊:與割點(diǎn)類似,如果一個(gè)點(diǎn)(u)的dfn值大于(不能等于,否則不一定)和它連接的一個(gè)節(jié)點(diǎn)(v)的low值,則說明這條邊(uv)為圖的一個(gè)割邊
變量解釋:
sum 指總共有幾個(gè)割點(diǎn)(邊)
割點(diǎn)code
void cutpoint(int u){int fl=0;//為特判準(zhǔn)備dfn[u]=low[u]=++cnt;//初始化for(int i=head[u];i;i=edge[i].next){//用鄰接表,下同int v=edge[i].to;if(!dfn[v]){cutpoint(v);low[u]=min(low[u],low[v]);if(u!=root&&low[v]>=dfn[u]&&!cpoint[u]) sum++,cpoint[u]=1;//不是根節(jié)點(diǎn)&&v的low值>=u的dfn值&&此點(diǎn)沒有算過if(u==root) fl++;//此時(shí)特判++}low[u]=min(low[u],dfn[v]); }if(fl>=2&&!cpoint[u]) sum++,cpoint[u]=1;//根節(jié)點(diǎn)若有兩棵子樹則是割點(diǎn) }割邊code
void cutedge(int u,int f){dfn[u]=low[u]=++cnt;for(int i=head[u];i;i=edge[i].next){int v=edge[i].to;if(!dfn[v]){cutedge(v,u);low[u]=min(low[u],low[v]);if(dfn[u]<low[v]) cedge[++sum]=i;//記錄邊的序號(hào)}else if(v!=f) low[u]=min(low[u],dfn[v]); //只有當(dāng)v不是u的上一個(gè)節(jié)點(diǎn)時(shí)可行} }完整模板code:
ps:這里就不打注釋了,核心就在上面的部分里
轉(zhuǎn)載于:https://www.cnblogs.com/Menteur-Hxy/p/9248051.html
總結(jié)
以上是生活随笔為你收集整理的Tarjan算法 (强联通分量 割点 割边)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 利用递归的方法求最大公约数和最小公倍数(
- 下一篇: http响应头中X-Frame-Opti