Tarjan算法求解一个无向图中的割点和桥问题
2016-10-27 13:29
239 查看
基本概念
割点:Articulation Point在无向连通图中,删除一个顶点v及其相连的边后,原图从一个连通分量变成了两个或多个连通分量,则称顶点v为割点,同时也称关节点(Articulation Point)。
双连通的图:一个没有关节点的连通图称为重连通图(biconnected graph)(双连通图)。
连通度:k,若在连通图上至少删去k 个顶点才能破坏图的连通性。
算法应用
算法应用:通信网络中,用来衡量系统可靠性,连通度越高,可靠性越高。求解方法
暴力求解,依次删除每一个节点v,用DFS(或者BFS)判断是否连通,再把节点加入图中。若用邻接表(adjacency list),需要做V次DFS,时间复杂度为O(V∗(V+E))O(V∗(V+E))Jarjan算法,只用一次DFS求解。
第一个方法不多说:
对于第二个方法
我们要维持两个数据结构:
dfn[u]:记录节点u在DFS过程中被遍历到的次序号。
low[u]:记录节点u或u的子树通过非父子边追溯到最早的祖先节点(即DFS次序号最小)。
其中对于low[u]的理解是这样的:当(u,v)为树边时,且v没有被访问过,则low[u]是min(low[u]和low[v])。当v被访问过,如果v不是u的父节点(如果是则说明有重边,不考虑),则(u,v)为回边,则low[u]取min(low[u], dfn[v])。
代码思路解析
#include<iostream> #include<vector> #include<fstream> using namespace std; #define N 201 vector<int>G ; bool visit ; int dfn ; int low ; int parent ; int min(int a, int b) { if (a < b) return a; else return b; } void input() { int n, m;//分别表示顶点数和边数 ifstream in("input.txt"); in >> n >> m; int a, b; for (int i = 1; i <= m; ++i) { in >> a >> b; G[a].push_back(b);/*邻接表储存无向边*/ G[b].push_back(a); } } void dfs(int u) { //记录dfs遍历次序 static int counter = 0; //记录节点u的子树数 int children = 0; visit[u] = true; //初始化dfn与low dfn[u] = low[u] = ++counter; for (int j = 0; j < G[u].size(); j++)//遍历与u相连的顶点 { int v = G[u][j]; if (!visit[v]) { children++; parent[v] = u;//u是v的父节点 dfs(v);//深度优先搜索v low[u] = min(low[u], low[v]);//等v完成深度优先搜索之后,low[u]记录节点u或u的子树通过非父子边追溯到最早的祖先节点(即DFS次序号最小) if (parent[u] == -1 && children > 1)//对根节点u,若其有两棵或两棵以上的子树,则该根结点u为割点; { cout << "1 articulation point: " << u<<endl; } if (parent[u] != -1 && low[v] >= dfn[u])//对非叶子节点u(非根节点),若其子树的节点均没有指向u的祖先节点的回边(条件low[v] >= dfn[u]表达的就是),说明删除u之后,根结点与u的子树的节点不再连通;则节点u为割点。 { cout << "2 articulation point: " << u << endl; //这样做,可能出现某个顶点多次输出,其实可以用一个指示变量来指示,做到顶点不重复输出 } if (low[v] >dfn[u]) //桥的条件 { cout << "Bridge " << v << " " << u << endl; } } else if (v != parent[u]) {//节点v已访问,则(u,v)为回边,且不是重边 low[u] = min(low[u], dfn[v]); } } } int main() { /*input(); memset(dfn, -1, sizeof(dfn)); memset(father, 0, sizeof(father)); memset(low, -1, sizeof(low)); memset(is_cut, false, sizeof(is_cut)); count();*/ memset(dfn, -1, sizeof(dfn)); memset(low, -1, sizeof(low)); memset(visit, false, sizeof(visit)); memset(parent, -1, sizeof(parent)); input(); dfs(1); system("pause"); return 0; }
参考
两个很好的讲解很详细的参考资料:http://www.cnblogs.com/en-heng/p/4002658.html
http://www.cnblogs.com/c1299401227/p/5402747.html
相关文章推荐
- 一个非线性约束问题的lingo求解
- 求解一个flapping问题,双端口可学一个MAC
- 怎样寻找最佳爱人:一个微积分求解的离散数学问题(建模)
- 求解组合问题的一个迭代算法
- 职责链模式和工厂模式混合求解一个简单的解密问题
- 一个分酒问题的求解
- 小弟在研究CUDA时出现一个问题,求解
- 一个“散点均匀分布”问题的求解
- 求解:SQL SERVER 2008的一个问题
- 一个C语言问题,求解
- 做一个项目是遇到的问题与求解!
- 一个难解的矩阵问题~~求解
- 挑战算法高手:一个Java算法问题求解
- 怎样寻找最佳爱人:一个微积分求解的离散数学问题
- 关于cuda编程的一个问题(求解)
- 一个sql语句的经典问题,求解!
- 一个搜索问题的求解
- 在线求解一个查询问题
- 一个关于移位的问题的多种算法求解
- 求解一个简单的创建单链表的问题为什么用二级指针 ?