博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Tarjan 算法&模板
阅读量:7061 次
发布时间:2019-06-28

本文共 3389 字,大约阅读时间需要 11 分钟。

Tarjan 算法

一.算法简介

Tarjan 算法一种由Robert Tarjan提出的求解有向图强连通分量的算法,它能做到线性时间的复杂度。

 

我们定义:

如果两个顶点可以相互通达,则称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通分量(strongly connected components)。

例如:在上图中,{1 , 2 , 3 , 4 } , { 5 } ,  { 6 } 三个区域可以相互连通,称为这个图的强连通分量。

Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。

再Tarjan算法中,有如下定义。

DFN[ i ] : 在DFS中该节点被搜索的次序(时间戳)

LOW[ i ] : 为i或i的子树能够追溯到的最早的栈中节点的次序号

当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).

三.算法模板

 
 
#include 
#define INF 0x3f3f3f3f#define ms(x,y) memset(x,y,sizeof(x))using namespace std;typedef long long ll;const double pi = acos(-1.0);const int mod = 1e9 + 7;const int maxn = 1e5 + 5;const int V = 150, E = 100500;struct Edge { int to, next;} edge[E];int head[V], num; //记录树的结构int idx; // 记录时间戳int top, S[V]; //记录堆栈int indeg[V], outdeg[V]; //记录入度、出度int low[V], dfn[V]; //low记录能追溯到最前面的点,dfn记录dfs序int belong[V], scc; //Belong[i] = a; 表示i这个点属于第a个连通分量 scc为连通分量个数bool vis[V]; //标记是否进栈int n;int addedge(int u, int v){ edge[num].to = v; edge[num].next = head[u]; head[u] = num++;}void tarjan(int u){ int v; dfn[u] = low[u] = ++idx; S[top++] = u; vis[u] = 1; for (int i = head[u]; i != -1; i = edge[i].next) { v = edge[i].to; if (dfn[v] == 0) //v还未遍历 { tarjan(v); low[u] = min(low[u], low[v]); //确保low[u]最小 } else if (vis[v]) //如果在堆栈中 { low[u] = min(low[u], dfn[v]); } } if (dfn[u] == low[u]) //表示找完一个连通分量 { ++scc; do { v = S[--top]; vis[v] = 0; belong[v] = scc; } while (u != v); }}int solve(){ scc = top = idx = 0; ms(dfn, 0); ms(vis, 0); for (int u = 1; u <= n; u++) { if (dfn[u] == 0) { tarjan(u); } } return scc;}void count_deg(){ ms(indeg, 0); ms(outdeg, 0); for (int u = 1; u <= n; u++) { for (int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].to; if (belong[u] != belong[v]) { indeg[belong[v]]++; //v所在的连通块入度++ outdeg[belong[u]]++; //u所在的连通块出度++ } } }}int main(){ //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); while (~scanf("%d", &n)) {
num=0;        ms(head, -1);        for (int u = 1; u <= n; u++)        {            int v;            while (~scanf("%d", &v) && v)            {                addedge(u, v);            }        }        solve();        if (scc == 1)        {            printf("1\n0\n");        }        else        {            count_deg();            int num_in = 0, num_out = 0;            for (int i = 1; i <= scc; i++)            {                if (indeg[i] == 0)                    num_in++;                if (outdeg[i] == 0)                    num_out++;            }            printf("%d\n%d\n", num_in, max(num_in, num_out));        }    }    return 0;}
 
 转自:http://www.cnblogs.com/shadowland/p/5872257.html 

 
 
 
 

转载于:https://www.cnblogs.com/Archger/p/8451563.html

你可能感兴趣的文章
思科ASA8.4.2 2层透明墙基本实施和配置用例
查看>>
第一个vue应用
查看>>
mail
查看>>
在JAVA中将项目转换为maven项目
查看>>
微信三大平台介绍
查看>>
实现级联查询
查看>>
js时钟
查看>>
Linux系统编程 --- 共享内存及内存映射【十全十美】
查看>>
如何创建一个swap文件
查看>>
mysql联合索引
查看>>
linux文本操作之---sed
查看>>
htmlcleaner 使用示例
查看>>
我的友情链接
查看>>
hystrix 源码分析--线程隔离
查看>>
我的友情链接
查看>>
经典24点算法大全
查看>>
nexus3 maven配置文件
查看>>
php使用nusoap创建WebService
查看>>
仿淘宝的滚动效果
查看>>
SQL2005开启远程连接功能
查看>>