Tarjan 算法介绍及用法

Tarjan 算法介绍及用法Tarjan 简介 这是一个有关图联通的算法 它基于 dfs 在解决有环的有向图或无向图的问题时 很多算法不好是操作 那么就先要将环进行缩点 将其转换为 DAG 有向无环图 或一棵树 然后问题应会迎刃而解常规操作 首先补几个概念 强连通 在一个 DAG 中 有 a b 两点 若 a 可以到达 b 且 b 可以到达 a 则 a b 即为强连通 强连通图 若在一个 DAG 中 任意两

Tarjan

简介:

  • 这是一个有关图联通的算法,它基于dfs 在解决有环的有向图或无向图的问题时,很多算法不好是操作…
  • 那么就先要将进行缩点,将其转换为DAG(有向无环图)或一棵,然后问题应会迎刃而解

常规操作:

具体用法:

常规的用法:缩点,割点,割边,点双,边双

缩点:
概念:化环为点
原理:通过栈来将环内存下,并在第二次访问到该环的根节点时,依次弹出,进行编号
模板(code):


1.在有向图中:

void tarjan(int x){ dfn[x]=low[x]=++tim; stk[++top]=x; vis[x]=1; SREP(i,0,E[x].size()){ int y=E[x][i]; if(!dfn[y]){ tarjan(y); chkmin(low[x],low[y]); } else if(vis[y]) chkmin(low[x],dfn[y]); } if(dfn[x]==low[x]){ tot++; do{ Id[stk[top]]=tot; vis[stk[top]]=0; }while(x!=stk[top--]); } }

2.在无向图中:

void tarjan(int x,int f){ dfn[x]=low[x]=++tim; stk[++top]=x; vis[x]=1; bool flag=1; SREP(i,0,E[x].size()){ int y=E[x][i]; if(y==f && flag){flag=0;continue;} if(!dfn[y]){ tarjan(y,x); chkmin(low[x],low[y]); } else if(vis[y]) chkmin(low[x],dfn[y]); } if(dfn[x]==low[x]){ tot++; do{ Id[stk[top]]=tot; vis[stk[top]]=0; }while(x!=stk[top--]); } }

然而一般题目多是无向图,接下来都用无向图了…

割点:
概念:若删掉某点后,原连通图分裂为多个子图,则称该点为割点。
原理:若low[y]>=dfn[x],则x为割点。因为low[y]>=dfn[x],则说明y通过子孙无法到达x的祖先。那么对于原图,去掉x后,必然会分成两个子图。

模板(code):

void tarjan(int x,int f){ dfn[x]=low[x]=++tim; stk[++top]=x; bool flag=1; int child=0; SREP(i,0,E[x].size()){ int y=E[x][i]; if(y==f && flag){flag=0;continue;} if(!dfn[y]){ child++; tarjan(y,x); chkmin(low[x],low[y]); if(low[y]>=dfn[x]){ cut[x]=1; //该点为割点  } } else chkmin(low[x],dfn[y]); } if(!f && child==1)cut[x]=0;//注意初始点,特判  }

割边(桥):
概念:删掉该边后,原图必然会分裂为多个子图
原理:若low[y]>dfn[x],则边(x,y)为桥。由割点同理可得。但是由于可能存在重边,需要把一条无向边拆成的两条标号相同的有向边,记录每个点的父亲到它的边的标号,如果边(x,y)是x的后向边,就不能用dfn[u]更新low[v]。这样如果遍历完v的所有子节点后,发现low[y]=dfn[y],说明x的后向边(x,y)为割边。
模板(code):


void tarjan(int x,int f){ dfn[x]=low[x]=++tim; stk[++top]=x; bool flag=1; SREP(i,0,E[x].size()){ int y=E[x][i]; if(y==f && flag){flag=0;continue;} if(!dfn[y]){ child++; tarjan(y,x); chkmin(low[x],low[y]); if(low[y]>dfn[x]){ mark[x][y]=mark[y][x]=1; //该边为割边  } } else chkmin(low[x],dfn[y]); } }

点双(点双连通分量):
概念:在求出所有的割点以后,把所有的割点删除,原图变成了多个连通块。
原理:在求割点的过程中就能把每个点双连通分支求出。通过栈存储双连通分支。在搜索图时,每找到一条后向边(非横叉边),就把这条边加入栈中。如果遇到某时满足dfn[x]<=low(y),说明x是一个割点,同时把边从栈顶一个个取出,直到遇到了边(x,y),取出的这些边与其关联的点,从而组成一个点双,一般再用个容器存一下每个点双。
模板(code):


void tarjan(int x,int f){ dfn[x]=low[x]=++tim; stk[++top]=x; vis[x]=1; bool flag=1; int child=0; SREP(i,0,E[x].size()){ int y=E[x][i]; if(y==f && flag){flag=0;continue;} if(!dfn[y]){ child++; tarjan(y,x); chkmin(low[x],low[y]); if(low[y]>=dfn[x]){ cut[x]=1; V[++tot].clear(); do{ vis[stk[top]]=0; Id[stk[top]]=tot; V[tot].pb(stk[top]); }while(y!=stk[top--]); V[tot].pb(x); solve();//解决该点双上的问题  } } else if(vis[y])chkmin(low[x],dfn[y]); } if(!f && child==1)cut[x]=0; }

边双(边双连通分量):
概念:在求出所有的割边以后,把所有的割边删除,原图变成多个连通块。不属于任何一个边双连通分支,其余的边和每个顶点都属于且只属于一个边双连通分支。
原理:与点双同理,在求割边的过程中就能把每条边双连通分支求出,也是通过栈,不过此时自然是存边了,而不是点,之后栈的弹出边界与点双也是同理的,这里就不再赘述。
模板(code):


void tarjan(int x,int fa){ dfn[x]=low[x]=++tim; LREP(x){ int y=E[i].y; if(E[i].flag||dfn[y]>=dfn[x])continue; E[i].flag=E[i^1].flag=1; stk[++top]=i; if(!dfn[y]){ tarjan(y,x); low[x]=min(low[x],low[y]); if(low[y]>dfn[x]){ tot++; do{ int k=stk[top]; if(mark[E[k].y]!=tot){ em[++cnt]=make_pair(tot,E[k].y); mark[E[k].y]=tot; } if(mark[E[k^1].y]!=tot){ em[++cnt]=make_pair(tot,E[k^1].y); mark[E[k^1].y]=tot; } Id[k>>1]=tot; }while(i!=stk[top--]); } } else low[x]=min(low[x],dfn[y]); } }

总结:

tarjan是一个非常使用且易记忆的算法,将一张有环图转换为DAG一棵树后,
也就变成了一个十分常规的问题了,然后就可以再运用合理的数据结构或算法将其迎刃而解!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/233823.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 黄聪:在C#中如何使用资源中的图片

    黄聪:在C#中如何使用资源中的图片

    2021年8月5日
    57
  • asp.net 跳转页面[通俗易懂]

    asp.net 跳转页面[通俗易懂]①response.redirect这个跳转页面的方法跳转的速度不快,因为它要走2个来回(2次postback),但他可以跳转到任何页面,没有站点页面限制(即可以由雅虎跳到新浪),同时不能跳过登录保护。但速度慢是其最大缺陷!redirect跳转机制:首先是发送一个http请求到客户端,通知需要跳转到新页面,然后客户端在发送跳转请求到服务器端。需要注意的是跳转后内部空间保存的所有数据信息将会

    2022年7月20日
    15
  • mapState与mapGetters语法糖

    mapState与mapGetters语法糖vuex中的mapState,mapGetters属于辅助函数,其实就是vuex的一个语法糖,使代码更简洁更优雅。<script>import{mapState,mapGetters}from”vuex”exportdefault{computed:{ //原先templateInfo(){ returnthis.$store.state.templateInfo},//现在

    2022年5月17日
    37
  • 计算机高配表要表格,为何高配电脑还会卡? 因为你没选择FreeSync套装

    计算机高配表要表格,为何高配电脑还会卡? 因为你没选择FreeSync套装可能有很多玩家在网络对战游戏中都遇到如此状况:电脑配置并不低,但游戏画面依然不够顺滑,不但经常卡顿,而且明明先瞄准敌人开枪,敌人没死而自己被秒掉。其实,这并不是因为玩家枪法太菜,问题在很大程度上出在玩家选择的显卡与显示器上。那到底玩家的显卡和显示器上到底有什么问题?让我们为大家分析一下吧。高配电脑可以提供高帧速,但并不一定无卡顿高配置的电脑当然能提供强劲的性能,在游戏中自然可以提供很高的帧速。但为…

    2022年6月1日
    34
  • typescript的泛型_泛型有什么用

    typescript的泛型_泛型有什么用泛型指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定具体类型的一种特性。引入下面创建一个函数,实现功能:根据指定的数量count和数据value,创建一个包

    2022年7月31日
    9
  • Springboot Mybatis使用pageHelper实现分页查询[通俗易懂]

    Springboot Mybatis使用pageHelper实现分页查询[通俗易懂]以下介绍实战中数据库框架使用的是mybatis,对整合mybatis此处不做介绍。使用pageHelper实现分页查询其实非常简单,共两步:一、导入依赖;二、添加配置;三、应用;那么开始,第一步:pom.xml添加依赖:<!–分页插件pagehelper–><dependency><groupId>com…

    2022年6月2日
    43

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号