这个算法是用来求解图的强连通份量的,这个是图论的一些知识,前段时间没有学,这几天在补坑...
算法
有向图中,尽量多的若干顶点组成的子图中,这些顶点都是相互可到达的,则这些顶点成为一个强连通份量spa
以下图所示,a、b、e以及f、g和c、d、h各自构成一个强联通份量3d
对于一个无向图的连通份量,从连通份量的任意一个顶点开始进行一次DFS,必定是能够遍历这个连通份量的全部定点的。因此,整个图的连通份量数就等价于咱们对于这个图找了几回起点(也就是咱们遍历这个图了几回DFS)。在这其中咱们每一次遍历中所获得的定点属于同一个连通份量。code
咱们为了求得这个图的强联通份量,咱们就须要对其进行DFS遍历,而顺序正遍历的DFS的过程是显然的,咱们仍须要一种遍历顺序来知足能够达到咱们可使得每个强联通份量均可以被遍历到且遍历的顺序是有序的算法。blog
DFS的逆后序遍历指的是假如到达了A节点且A节点并无被访问过,就去遍历与A节点相连的且没有被访问的其余节点,而后将这些节点假如栈中,最后这个栈从栈顶到栈底的顺序DFS逆后序遍历。it
对于任意的两个强联通份量之间是不可能存在有两条路互相链接造成环的(这是显然的,由于若是有环咱们即须要将其当作是同一个强联通份量)。class
因此求解的步骤能够分为如下两步:搜索
对原图取反,从任意一个顶点开始对反向图进行逆后续DFS遍历遍历
按照逆后续遍历中栈中的顶点出栈顺序,对原图进行DFS遍历,一次DFS遍历中访问的全部顶点都属于同一强连通份量。map
假设这一个图是须要求解强联通份量的图
那么对于这个图进行取反就获得了这个图:
一共有两种DFS的可能性:
从A点开始:
假设DFS从位于强连通份量A中的任意一个节点开始。那么第一次DFS完成后,栈中所有都是强连通份量A的顶点,第二次DFS完成后,栈顶必定是强连通份量B的顶点。
从B点开始:
假设DFS从位于强连通份量B中的任意一个顶点开始。显然咱们只须要进行一次DFS就能够遍历整个图,因为是逆后续遍历,那么起始顶点必定最后完成,因此栈顶的顶点必定是强连通份量B中的顶点。
因此对于每一次DFS,都会有一个对应的强联通份量,证毕
void dfsone(int x) { vst[x] = 1; for(int i=1;i<=n;i++) if(!vst[i] && map[x][i]) dfsone(i); d[++t] = x; //最后访问的节点 } //d[i] = x : i -> 组 x -> 节点 void dfstwo(int x) { vst[x] = t; for(int i=1;i<=n;i++) if(!vst[i] && map[i][x]) dfstwo(i); } void kosaraju() { int t = 0; for(int i=1;i<=n;i++) if(!vst[i]) dfsone(i); memset(vst,0,sizeof(vst)); t = 0; for(int i=n;i>=1;i--) if(!vst[d[i]]) { t++; dfstwo(d[i]); } }
以上就是Kosaraju
接下来开始Tarjan
Tarjan是一种基于DFS的算法 ,图中每一个强连通份量为搜索树的一棵子树
咱们在DFS的过程当中会遇到四种边:
树枝边:一条通过的边,即DFS搜索树上的一条边
前向边:与DFS方向一致,从某个节点指向其子孙的边
后向边:与DFS方向相反,从某个节点指向其祖先的边
横叉边:从某个节点指向搜索树中另外一子树的某节点的边
DFN[i]:在DFS中该节点被搜索的次序(时间戳)
LOW[i]:为i或i的子树可以追溯到的最先的栈中节点的次序号
若是(u,v)为树枝边,u为v的父节点,则 LOW[u] = min(LOW[u],LOW[v])
若是(u,v)为后向边或者是指向栈中节点的横叉边,则 LOW[u] = min(LOW[u],DFN[v])
当节点u的搜索过程结束后,若是当DFN[ i ]==LOW[ i ]时,为i或i的子树能够构成一个强连通份量。
以1为Tarjan 算法的起始点,如图
顺次DFS搜到节点6
回溯时发现LOW[ 5 ]==DFN[ 5 ] , LOW[ 6 ]==DFN[ 6 ] ,则{ 5 } , { 6 } 为两个强连通份量。回溯至3节点,拓展节点4.
拓展节点1 , 发现1再栈中更新LOW[ 4 ],LOW[ 3 ] 的值为1
回溯节点1,拓展节点2
自此,Tarjan Algorithm 结束,{1 , 2 , 3 , 4 } , { 5 } , { 6 } 为图中的三个强连通份量。
不难发现,Tarjan Algorithm 的时间复杂度为O(E+V).
void tarjan(int x) { dfn[u] = low[u] = ++num; st[++top] = u; for(int i=fir[u];i;i=nex[i]) { int v = to[i]; if(!dfn[i]) { tarjan(v); low[u] = min(low[u],low[v]); } else if(! co[v]) low[u] = min(low[u],dfn[v]); } if(low[u] == dfn[u]) { co[u] = ++col; while(st[top] != u) { co[st[top]] = col; top--; } top--; } }