对于连通无向图,我门称其一个子图是双连通分量,是指任意两点u,v,存在一个圈包含u,v。与无向图相关联的还有关节点x,是指去掉x,图不连通;桥(u,v)是指去掉这条边,图不连通。
求解算法的要义在于首先要理解:/*
history:
2014.04.25 StringBuffer out 改成 LinkedList<Integer> out
2014.04.29 add Tarjan
*/
import java.io.*;
import java.util.*;
public class Graph
{
int n; /*顶点数*/
int[][] adjList;
int[] edgeCnt, edgeCap;
void trimToSize()
{
int i;
for(i = 0; i < n; ++i){
adjList[i] = Arrays.copyOf(adjList[i], edgeCnt[i]);
}
edgeCnt = null; edgeCap = null;
}
int addEdge(int v, int w)
{
if(edgeCnt[v] >= edgeCap[v]){
int newCap = edgeCap[v] + 10;
adjList[v] = Arrays.copyOf(adjList[v], newCap);
edgeCap[v] = newCap;
}
adjList[v][edgeCnt[v]++] = w;
return 0;
}
int[] getNeighbors(int v)
{
return adjList[v];
}
void dfs(int v, byte[] color, LinkedList<Integer> out)
{
int i, w = 0;
int[] nei = getNeighbors(v);
color[v] = 1;
for(i = 0; i < nei.length; ++i){
w = nei[i];
if(0 == color[w])dfs(w, color, out);
else if(1 == color[w]){/*a cycle is found.*/}
}
color[w] = 2;
out.addFirst( v);
//System.out.printf("v = %d out = %s %n", v, out);
}
void topoSort(byte[] color, LinkedList<Integer> out )
{
int i;
out.clear();
Arrays.fill(color, (byte)0);
for(i = 0; i < n; ++i){
if(0 == color[i])dfs(i, color, out);
}
}
void bfs(int v, byte[] color, LinkedList<Integer> out)
{
int i, w;
int[] nei;
LinkedList<Integer> qu = new LinkedList<Integer>();
out.clear();
Arrays.fill(color, (byte)0);
qu.push(v);
while(!qu.isEmpty()){
v = qu.pop();
out.addLast(v);
color[v] = 1; /*visited*/
nei = getNeighbors(v);
for(i = 0; i < nei.length; ++i){
w = nei[i];
if(0 == color[w])qu.push(w);
}
}
}
public Graph(int vertexNum, int edgeNum, Scanner in) throws IOException
{/*假设输入顶点数,边数,之后是m对边(v,w)*/
int i, v, w;
n = vertexNum;
adjList = new int[n][0];
edgeCnt = new int[n];
edgeCap = new int[n];
for(i = 0; i < n; ++i){
edgeCnt[i] = 0;
edgeCap[i] = 10;
adjList[i] = new int[edgeCap[i]];
}
for(i = 0; i < edgeNum; ++i){
v = in.nextInt(); // - 1;
w = in.nextInt(); // - 1;
addEdge(v, w);
}
trimToSize();
}
}
class Tarjan
{
Graph g;
int n;
int[] dfn; /*每个顶点的dfn值*/
int[] low;/*每个顶点的low值*/
int[] stk;/*存储分量顶点的栈*/
int stp;
int index; /*搜索次序*/
int[] comp; /*标记每个顶点属于哪个连通分量*/
int compTag;
int[] cut;/*标记每个顶点是否为割点*/
int son; /*根节点孩子个数,大于2则根节点为关节点*/
int beginVertex;
public Tarjan(Graph gRef)
{/*common context init*/
g = gRef;
n = g.n;
dfn = new int[n];
low = new int[n];
}
public void SCC()
{/*求有向图的强连通分量*/
int i;
Arrays.fill(dfn, 0);
Arrays.fill(low, 0);
stk = new int[n]; stp = 0;
comp = new int[n]; compTag = 0;
index = 0;
i = 0; //2;
for(; i < n; ++i){
if(0 == dfn[i])strongConnect(i);
}
stk = null;
comp = null;
}
void strongConnect(int x)
{
int[] adj;
int i, y;
stk[stp++] = x;
dfn[x] = low[x] = ++index;
adj = g.getNeighbors(x);
for(i = 0; i < adj.length; ++i){
y = adj[i];
if(0 == dfn[y]){
strongConnect(y);
low[x] = Math.min(low[x], low[y]);
}else if((dfn[y] < dfn[x]) && (0 == comp[y])){
low[x] = Math.min(low[x], dfn[y]);
}
}
if(low[x] == dfn[x]){
++compTag;
System.out.printf("SCC(%d): ", compTag);
do{
y = stk[--stp];
comp[y] = compTag;
System.out.printf("%d:(%d,%d), ", y, dfn[y], low[y]);
}while(y != x);
System.out.printf("%n");
}
}
public void BCC()
{/*求无向图的双连通分量,*/
int i;
Arrays.fill(dfn, 0);
Arrays.fill(low, 0);
stk = new int[4*n]; stp = 0;
cut = new int[n];
beginVertex = 0; son = 0;
index = 0;
biConnect(beginVertex, -1);
if(son > 1)cut[beginVertex] = 1;
System.out.printf("Cut: ");
for(i = 0; i < n; ++i)if(cut[i] > 0)System.out.printf("%d, ", i);
System.out.printf("%n");
stk = null;
cut = null;
}
void biConnect(int x, int father)
{
int i, y, x1, y1;
int[] nei;
dfn[x] = low[x] = ++index;
nei = g.getNeighbors(x);
for(i = 0; i < nei.length; ++i){
y = nei[i];
if(y == father)continue;
if(0 == dfn[y]){
stk[stp++] = x; stk[stp++] = y;
biConnect(y,x);
low[x] = Math.min(low[x], low[y]);
if(low[y] >= dfn[x]){
System.out.printf("BCC(%d,%d): ", x, y);
do{
y1 = stk[--stp]; x1 = stk[--stp];
System.out.printf("<%d:(%d,%d),%d:(%d,%d)>, ", x1, dfn[x1], low[x1], y1, dfn[y1], low[y1]);
}while(dfn[x1] >= dfn[y]);
System.out.printf("%n");
}
if(x == beginVertex)++son;
if(x != beginVertex && low[y] >= dfn[x])cut[x] = 1;
if(low[y] > dfn[x])System.out.printf("Bridge <%d %d> %n", x, y);
}else if(dfn[y] < dfn[x]){
stk[stp++] = x; stk[stp++] = y;
low[x] = Math.min(low[x], dfn[y]);
}
}
}
}
class Test
{
public static void main (String[] arg) throws IOException
{
int n, m;
byte[] color; /*for dfs: 0--white, 1--gray, 2--black*/
LinkedList<Integer> out = new LinkedList<Integer>();
Scanner in = new Scanner(System.in);
n = in.nextInt();
m = in.nextInt();
color = new byte[n];
Graph g = new Graph(n, m, in);
in.close();
in = null;
Arrays.fill(color, (byte)0);
g.dfs(0, color, out);
System.out.println("dfs: " + out);
g.bfs(0, color, out);
System.out.println("bfs: " + out);
g.topoSort(color, out);
System.out.println("topoSort: " + out);
color = null;
out = null;
Tarjan t = new Tarjan(g);
t.SCC();
t.BCC();
}
}
对于这个图的输出结果:
ludi@msys ~/java
$ cat in.txt
8 14
0 1
1 2 1 4 1 5
2 3 2 6
3 2 3 7
4 0 4 5
5 6
6 5 6 7
7 7
ludi@msys ~/java
$ javac -encoding UTF-8 Graph.java && cat in.txt | java Test
dfs: [0, 1, 4, 2, 6, 5, 3, 7]
bfs: [0, 1, 5, 6, 7, 4, 2, 3]
topoSort: [0, 1, 4, 2, 6, 5, 3, 7]
SCC(1): 7:(5,5),
SCC(2): 5:(7,6), 6:(6,6),
SCC(3): 3:(4,3), 2:(3,3),
SCC(4): 4:(8,1), 1:(2,1), 0:(1,1),
BCC(3,7): <3:(4,4),7:(5,5)>,
Bridge <3 7>
BCC(2,3): <2:(3,3),3:(4,4)>,
Bridge <2 3>
BCC(6,5): <6:(6,6),5:(7,7)>,
Bridge <6 5>
BCC(2,6): <6:(6,5),7:(5,5)>, <2:(3,3),6:(6,5)>,
Bridge <2 6>
BCC(1,2): <1:(2,2),2:(3,3)>,
Bridge <1 2>
BCC(0,1): <4:(8,1),5:(7,7)>, <4:(8,1),0:(1,1)>, <1:(2,1),4:(8,1)>, <0:(1,1),1:(2,1)>,
Cut: 1, 2, 3, 6,
对这个无向图的输出结果:
5 10
0 1 0 4
1 0 1 2
2 1 2 3 2 4
3 2
4 0 4 2
$ javac -encoding UTF-8 Graph.java && cat in.txt | java Test
dfs: [0, 1, 2, 4, 3]
bfs: [0, 4, 2, 3, 1, 1]
topoSort: [0, 1, 2, 4, 3]
SCC(1): 4:(5,1), 3:(4,3), 2:(3,1), 1:(2,1), 0:(1,1),
BCC(2,3): <2:(3,3),3:(4,4)>,
Bridge <2 3>
BCC(0,1): <4:(5,1),0:(1,1)>, <2:(3,1),4:(5,1)>, <1:(2,1),2:(3,1)>, <0:(1,1),1:(2,1)>,
Cut: 2,
原文地址:http://blog.csdn.net/deyangliu/article/details/24734649