对于连通无向图,我门称其一个子图是双连通分量,是指任意两点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