22.1-6 给出O(V)时间算法判断有向图G是否存在一个通用汇点(universal sink)。通用汇点指的是入度为|V|-1,出度为0的节点。
思路:
考虑图的邻接矩阵A,假设i为通用汇点,则对于0<=j<n,有A[i][j]=0,且对于所有0<=k<n and k != i,有A[k][i] = 1,注意这里k!=i
相当于在子矩阵A[0..i][0..i]的右边和下边造了一个围墙,最下边全是0,最右边除了右下角外全是1
那么可以令游标从矩阵左上角开始,遇1向下,逢0向右,直到行或列超出矩阵A的界限,注意不是子矩阵。此时若游标所在行没有超出界限,那和这个行号 r 就可能是通用汇点编号。
接下来再根据定义验证 r 是否就是通用汇点。因为可能存在以下的情况:
x x x x 1 x x x x x x x 1 x x x x x x x 1 x x x x x x x 1 x x x 0 0 0 0 0 0 0 0 x x x x 0 x x x x x x x 0 x x x x x x x 0 x x x
矩阵第5行所表示的节点显然不是通用汇点。
22.2-7 职业摔跤手划分
思路:可归结为对先广生成树进行着色,相邻层颜色不同。若发现有连接相同颜色节点的回边,则划分失败。发生这种情况,可能是相同层内的节点间有边,若与其他非相邻层间同色节点之间有边。
最后,相同颜色的划分为一组,划分完毕。
22.2-8 树的直径
思路:任意节点出发进行先广遍历,找到最长路径的末端节点u,再从u出发进行先广遍历,最长路径即为树的直径。
22.2-9 无向图G,找出分别以正、反向通过每条边的路径
思路:任意节点出发,深度优先遍历,节点可以访问任意次,但保持每条边只访问一次,在此过程中不断把访问到的边在前进方向末端的节点加入到路径中。每处理完一棵先深生成子树,再把根节点加入到路径。直到处理完的有子树,并返回到根节点。
22.3-13 对于有向图G=(V,E)来说,如果u->v意味着图G至多包含一条从u到v的简单路径,则图G是单连通图。给出算法判断一个有向图是否是单连通图。
注:本题解法参考了http://blog.csdn.net/wdq347/article/details/11096945,涉及到的理论证明不再详述,只列出思路。
首先说简单解法,依次从每个点出发进行DFS,出发前,把全部节点置为白色。每棵DFS树中只要发现forward edge或树内的cross edge,则图G必不是单连通图。
参考文章中给出了对于稠密图的优化解法。
简单来说,就是分别考虑图G的每个强连通分量,利用如下定理判断连通分量是否是单连通图(以下定理来自参考文章)
定理:图G是强连通图且DFS不含有forward edge或cross edge,G不是单连通图当且仅当DFS树满足以下任意一个条件:
(1) 存在点v至少有两条back edge;
(2) 存在点v只有一条back edge,且v的某个子结点x,low[x] <dfn[v]
(3) 存在点v,v至少有两个子结点x,y,low[x] < dfn[v],low[y] <dfn[v]
情况1表示v到某个祖先节点至少有两条路径,图G必不是单连通图;
情况2表示v的某个子节点与v的某个祖先存在路径,再加上v有一条back edge,则v到某个祖先节点(不一定是前面提到的那个祖先节点)存在两条路径;
情况3表示v至少有两个子节点通向了祖先节点,即v到某个祖先节点至少存在两条路径。
可惜的是,文章中给的代码只适用于强连通图或强连通分量,并没有继续处理分量图的过程。
这里补上我对后续处理分量图的理解。分量图,从直观上理解,就是把每个强连通分量分别压缩为一个点后形成的图。需要注意的是,压缩为一个点后,原先这个分量的入边都指向压缩后的点;而分量的出边则都从该点出发。分量图是一个有向无环图,那么就可以直接使用前面提到的简单解法进行求解。
总结一下,对于稀疏图,可直接使用简单解法,复杂度为O(V+E);对于稠密图,可使用参考文章提出的解法,先判断每个强连通分量是否为单连通图,若全是,则再判断分量图是否为单连通图,复杂度为O(V*V)
22.5-7 给定有向图G(V,E),所有结点对之间有单向或双向路径,则图G半连通。请判断一个图是否半连通。
http://blog.csdn.net/wdq347/article/details/9995153 提到了一个引理:有向无环图G(V,E),G是半连通的当且仅当有一条路径,这条路径上有图G中所有点。
接下来提到了解决方法,即先得到图G的分量图,再得到分量图的拓扑序列,若序列中任意相邻两点间有边(方向一定是序列中靠前的节点指向靠后的节点),则分量图是半连通图。又,强连通图也是半连通图,那么原始图每个强连通分量是半连通图,所以原始图也是半连通图。