Tarjar算法可以解决很多连通性问题,我的上一篇文章是用Tarjan解决强联通分量问题。
我们用dfn[ ]数组记录一个节点的遍历编号,也就是第几次遍历到的点。low[ ]代表一个节点能到达最小的点,不经过父节点。
比如
1.第一种情况是判定普通节点是否为割点。我们从节点1做为起点开始遍历,到2这个点发现有其他路可以到1。同理3节点也有路到他的祖先。这时暂时判定3不是割点,但是2还有为遍历的点。到节点4的时候,发现low[4]>=dfn[2],且2不是起始节点,那么我们判定2是割点。
2.第二种情况是判定起始节点是否为割点。我们从节点4开始,会发现low[2]>=dfn[4],如果按照上面的方法,4就是割点。很显然不是,对于起始节点的判定是,只要起始节点的度为2及以上,那么起始节点就是割点。就像我们从1开始一样,1不被判定为割点。
下面是一道模板题:洛谷 【模板】割点(割顶)
详细代码附上:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.ArrayList;
public class Temp {
static int num=0;
static ArrayList<Integer>[] g;
static int[] dfn,low;
static boolean[] cut;
public static void main(String[] args) throws IOException {
StreamTokenizer in=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
int N,M,u,v;
in.nextToken();N=(int)in.nval;
in.nextToken();M=(int)in.nval;
g=new ArrayList[N+1];
dfn=new int[N+1];
low=new int[N+1];
cut=new boolean[N+1];
for(int i=0;i<=N;i++)
g[i]=new ArrayList<Integer>();
for(int i=0;i<M;i++)
{
in.nextToken();u=(int)in.nval;
in.nextToken();v=(int)in.nval;
g[u].add(v);g[v].add(u);
}
//上面都是初始化
for(int i=1;i<=N;i++)
if(dfn[i]==0)
Tarjan(i, i);
for(int i=1;i<=N;i++)
if(cut[i])
{
pw.println(i);
pw.flush();
}
pw.flush();
}
//模板开始
static void Tarjan(int x,int farther)
{
int t;
dfn[x]=low[x]=++num;
for(int i=0;i<g[x].size();i++)
{
t=g[x].get(i);
if(dfn[t]==0)
{
Tarjan(t, farther);
low[x]=Math.min(low[x],low[t]);
if(low[t]>=dfn[x]&&x!=farther)
cut[x]=true;
}
else
low[x]=Math.min(low[x], dfn[t]);
}
if(g[x].size()>=2&&x==farther) //size<2的话代表这个节点是度为1的节点,不可能是割点
cut[x]=true;
}
//模板结束
}