简介
最近在看一些图相关的问题。实际上关于图相关的研究和问题已经非常多了。在前面的几篇文章里,我也谈到过图的定义、遍历法,扩展树生成和最短路径等问题。 除了这些问题及应用以外,还有一些比较常见的问题,虽然难度不大,不过经常会在一些情况下碰到。不仔细去考虑的话还是比较难解决的。这篇文章里重点要讨论解决的几个问题分别是检测图的连通性、图中间环的检测和二分图的检测。
图的连通性
判断一个图的连通性,从概念上来说,就是如果一个图是连通的,那么对于图上面的任意两个节点i, j来说,它们相互之间可以通过某个路径连接到对方。比如下图:
在这个图里,任意的两个节点都可以通过一个路径到达对方。而对于非连通的图来说,它相当于将一个图分割成多个独立的部分,每个部分之间没有任何联系,一个典型的示例如下图:
在这个图里,7,8组成的部分以及9到12所组成的部分它们都是互相隔离的。那么如果要检查和判断一个图是否为连通的,该用什么办法呢?
判断图是否连通
如果仅仅是判断一个图是否为连通的,结合前面讨论图的基础遍历方法,可以有如下的方法。在前面图遍历的方法过程中,我们是从一个指定的点开始,通过不同的策略去遍历这个图,有深度遍历和广度遍历。每次经过一个节点的时候,首先判断一下这个节点是否已经访问过了,如果没有访问过,则这个节点可以作为下一次继续遍历的候选。因为如果这个图是连通的话,这种方法最终会覆盖到整个图。所以可以采用一种计数统计的方式来实现。比如说每次访问一个以前没有遍历的节点,则将对应的计数加一。这样当最后遍历结束后,如果统计的节点和图本身的节点一样的话,表示这个图是连通的,否则表示不连通。在前面的图定义里有相关实现,这里把部分代码给转贴过来。
深度优先遍历:
public class DepthFirstSearch {
private boolean[] marked;
private int count;
private final int s;
public DepthFirstSearch(Graph g, int s) {
marked = new boolean[g.getVertices()];
this.s = s;
dfs(g, s);
}
private void dfs(Graph g, int v) {
marked[v] = true;
count++; //计数,统计访问过的节点
for(int w : g.adj(v))
if(!marked[w]) {
dfs(g, w);
}
}
public boolean marked(int w) {
return hasPathTo(w);
}
public int count() {
return count;
}
}
广度优先遍历:
private void bfs(Graph g, int s) {
Queue<Integer> queue = new LinkedList<I