以下内容为个人理解,有错误地方还请指出。
须知概念:
Dfs搜索树,回边,交叉边(无向图的dfs树不存在交叉边);
dfn[u] :u的dfs序
low[u] :从u或u的子孙出发,通过回边可以到达的最小的dfs序
对于Tarjan算法中,我们得到了dfn和low两个数组,
low[u]:=min(low[u],dfn[v])——(u,v)为回边,v不是u的子树;
low[u]:=min(low[u],low[v])——(u,v)为树枝边,v为u的子树;
下边对其进行讨论:
若low[v]>=dfn[u],则u为割点,u和它的子孙形成一个块。因为这说明u的子孙不能够通过其他边到达u的祖先,这样去掉u之后,图必然分裂为两个子图。
若low[v]>dfn[u],则(u,v)为割边。理由类似于上一种情况。
该算法要求图中不能有重边;
Tarjan 算法的任务就是算出每个点的low值,然后根据图的性质去使用low值。
可以用tarjan算法求解缩点,割点,割边,点双连通分量,边双联通分量,强连通分量等。
http://blog.csdn.net/u013569304/article/details/51146779
http://blog.csdn.net/u013569304/article/details/51146686
代码实现
//vector<int>head[maxn]; //耗时高
typedef vector<int>ve;
vector<ve>head(maxn);
int dep_tmp;
int dfn[maxn],low[maxn],dep[maxn],pre[maxn];//分别为dfs序,low值,节点深度,前继数组;
bool vis[maxn],cut_edge[maxn],cut_dot[maxn];//分别为标记数组,割边标记(记录深度较深的一段,(u,pre[u])就是割边),割点标记
int num;//割边数量
void tarjan_dfs(int rt,int father)
{
int son=0;//rt的孩子数
dfn[rt]=low[rt]=dep_tmp++;
dep[rt]=dep[father]+1;
vis[rt]=true;
for(int i=0,len=head[rt].size();i<len;++i)
{
int v=head[rt][i];
if(!vis[v])
{
son++;
pre[v]=rt;
tarjan_dfs(v,rt);
low[rt]=min(low[rt],low[v]);
if(low[v]>dfn[rt])
{
cut_edge[v]=true;
num++;
}
}
else if(v!=father)
low[rt]=min(low[rt],dfn[v]);
// if(low[v]>dfn[rt])//在这记录割边,如果有重边结果会错(偏大)
// {
// cut_edge[v]=true;
// num++;
// }
// if( (rt!=root && low[v]>=dfn[rt]) || (rt==root && son>1))//割点
// {
// cut_dot[rt]=true;//标记割点
// }
}