tarjan算法的原理

tarjan算法的原理

Tarjan算法原理及应用

一、引言

Tarjan算法是一种用于图的深度优先搜索的算法,它可以在无向图或有向图中找到所有强连通分量。这个算法由美国计算机科学家Robert Tarjan于1972年提出,被广泛应用于图论和算法领域。本文将介绍Tarjan算法的原理及其应用。

二、Tarjan算法原理

1. 深度优先搜索

Tarjan算法是基于深度优先搜索的,深度优先搜索是一种图遍历算法,从一个顶点出发,沿着一条路径一直往下走,直到不能再走为止,然后回溯到前一个顶点,继续向未走过的路径探索。这种搜索方式可以用递归或栈来实现。

2. 强连通分量

在图中,如果任意两个顶点之间都存在路径,那么它们构成一个强连通分量。强连通分量是图中的一个重要概念,它可以帮助我们理解图结构的特性。

3. Tarjan算法步骤

Tarjan算法通过深度优先搜索来寻找强连通分量,其具体步骤如下:(1)初始化。将所有顶点标记为未访问状态,定义一个栈来保存已

经访问的顶点。

(2)深度优先搜索。从图中的任意一个未访问的顶点开始进行深度优先搜索。

(3)标记顶点。在搜索过程中,对每个顶点进行标记,记录其访问顺序(也称为时间戳)和能够到达的最小时间戳。

(4)寻找强连通分量。当一个顶点的访问顺序等于能够到达的最小时间戳时,说明它是一个强连通分量的根节点。通过弹出栈中的顶点,可以找到该强连通分量中的所有顶点。

三、Tarjan算法应用

Tarjan算法在图论和算法设计中有着广泛的应用,下面介绍几个常见的应用场景:

1. 强连通分量的查找

Tarjan算法可以高效地找到图中的所有强连通分量。这对于解决一些实际问题非常有用,比如社交网络中的群组划分、电路中的等价关系判断等。

2. 有向图的可达性分析

在有向图中,Tarjan算法可以用来判断两个顶点之间是否存在路径。这对于解决一些路径相关的问题非常有帮助,比如寻找关键路径、判断死锁等。

3. 编译器优化

Tarjan算法可以用于编译器的优化过程中,通过判断变量的依赖关系来进行代码重排和性能优化。

4. 强连通分量的缩点

在一些场景中,我们可能并不关心具体的强连通分量,而是希望将它们合并成一个顶点,从而得到一个更简化的图结构。Tarjan算法可以用来实现强连通分量的缩点操作。

四、总结

Tarjan算法是一种用于图的深度优先搜索的算法,它可以高效地找到图中的所有强连通分量。该算法在图论和算法设计中有着广泛的应用,可以解决诸如强连通分量的查找、有向图的可达性分析、编译器优化等问题。通过运用Tarjan算法,可以更好地理解和分析图结构,为实际问题的解决提供有效的方法与思路。

五、参考文献

[1] Tarjan R E. Depth-first search and linear graph algorithms[J]. SIAM journal on computing, 1972, 1(2): 146-160.

[2] Cormen, T. H., Leiserson, C. E., Rivest, R. L., & Stein,

C. (2009). Introduction to algorithms. MIT press.

求强连通分量的Kosaraju算法和Tarjan算法的比较 by ljq

求强连通分量的Kosaraju算法和Tarjan算法的比较 一、定义 在有向图中,如果两个顶点vi,vj间有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly connected)。如果有向图的每两个顶点都强连通,则称该有向图是一个强连通图。非强连通的有向图的极大强连通子图,称为强连通分量(strongly connected components)。 而对于一个无向图,讨论强连通没有意义,因为在无向图中连通就相当于强连通。 由一个强连通分量内的所有点所组成的集合称为缩点。在有向图中的所有缩点和所有缩点之间的边所组成的集合称为该有向图的缩图。 例子: 原图: 缩图: 上面的缩图中的 缩点1包含:1、2,;缩点2包含:3; 缩点3包含:4;缩点4包含:5、6、7。

二、求强连通分量的作用 把有向图中具有相同性质的点找出来,形成一个集合(缩点),建立缩图,能够方便地进行其它操作,而且时间效率会大大地提高,原先对多个点的操作可以简化为对它们所属的缩点的操作。 求强连通分量常常用于求拓扑排序之前,因为原图往往有环,无法进行拓扑排序,而求强连通分量后所建立的缩图则是有向无环图,方便进行拓扑排序。 三、Kosaraju算法 时间复杂度:O(M+N)注:M代表边数,N代表顶点数。 所需的数据结构:原图、反向图(若在原图中存在vi到vj的有向边,在反向图中就变成为vj到vi的有向边)、标记数组(标记是否遍历过)、一个栈(或记录顶点离开时间的数组)。 算法描叙: 步骤1:对原图进行深度优先遍历,记录每个顶点的离开时间。 步骤2:选择具有最晚离开时间的顶点,对反向图进行深度优先遍历,并标记能够遍历到的顶点,这些顶点构成一个强连通分量。 步骤3:如果还有顶点没有遍历过,则继续进行步骤2,否则算法结束。 hdu1269(Kosaraju算法)代码: #include #include const int M=10005; struct node { int vex; node *next; }; node *edge1[M],*edge2[M]; bool mark1[M],mark2[M]; int T[M],Tcnt,Bcnt; void DFS1(int x)

组合数学

组合数学概述 组合数学,又称为离散数学,但有时人们也把组合数学和图论加在一起算成是离散数学。组合数学是计算机出现以后迅速发展起来的一门数学分支。计算机科学就是算法的科学,而计算机所处理的对象是离散的数据,所以离散对象的处理就成了计算机科学的核心,而研究离散对象的科学恰恰就是组合数学。组合数学的发展改变了传统数学中分析和代数占统治地位的局面。现代数学可以分为两大类:一类是研究连续对象的,如分析、方程等,另一类就是研究离散对象的组合数学。组合数学不仅在基础数学研究中具有极其重要的地位,在其它的学科中也有重要的应用,如计算机科学、编码和密码学、物理、化学、生物等学科中均有重要应用。微积分和近代数学的发展为近代的工业革命奠定了基础。而组合数学的发展则是奠定了本世纪的计算机革命的基础。计算机之所以可以被称为电脑,就是因为计算机被人编写了程序,而程序就是算法,在绝大多数情况下,计算机的算法是针对离散的对象,而不是在作数值计算。正是因为有了组合算法才使人感到,计算机好象是有思维的。 组合数学不仅在软件技术中有重要的应用价值,在企业管理,交通规划,战争指挥,金融分析等领域都有重要的应用。在美国有一家用组合数学命名的公司,他们用组合数学的方法来提高企业管理的效益,这家公司办得非常成功。此外,试验设计也是具有很大应用价值的学科,它的数学原理就是组合设计。用组合设计的方法解决工业界中的试验设计问题,在美国已有专门的公司开发这方面的软件。最近,德国一位著名组合数学家利用组合数学方法研究药物结构,为制药公司节省了大量的费用,引起了制药业的关注。 在1997年11月的南开大学组合数学研究中心成立大会上,吴文俊院士指出, 每个时代都有它特殊的要求,使得数学出现一个新的面貌,产生一些新的数学分支,组合数学这个新的分支也是在时代的要求下产生的。最近,吴文俊院士又指出,信息技术很可能会给数学本身带来一场根本性的变革,而组合数学则将显示出它的重要作用。杨乐院士也指出组合数学无论在应用上和理论上都具有越来越重要的位置,它今后的发展是很有生命力,很有前途的,中国应该倡导这个方面的研究工作。万哲先院士甚至举例说明了华罗庚,许宝禄,吴文俊等中国老一辈的数学家不仅重视组合数学,同时还对组合数学中的一些基本问题作了重大贡献。迫于中国组合数学发展自身的需要,以及中国信息产业发展的需要,在中国发展组合数学已经迫在眉睫,刻不容缓。 2. 组合数学与计算机软件 随着计算机网络的发展,计算机的使用已经影响到了人们的工作,生活,学习,社会活动以及商业活动,而计算机的应用根本上是通过软件来实现的。我在美国听到过一种说法,将来一个国家的经济实力可以直接从软件产业反映出来。我国在软件上的落后,要说出根本的原因可能并不是很简单的事,除了技术和科学上

Tarjan算法合集

基本概念: 1.割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点。 2.割点集合:在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成多个连通块,就称这个点集为割点集合。 3.点连通度:最小割点集合中的顶点数。 4.割边(桥):删掉它之后,图必然会分裂为两个或两个以上的子图。 5.割边集合:如果有一个边集合,删除这个边集合以后,原图变成多个连通块,就称这个点集为割边集合。 6.边连通度:一个图的边连通度的定义为,最小割边集合中的边数。 7.缩点:把没有割边的连通子图缩为一个点,此时满足任意两点之间都有两条路径可达。注:求块<>求缩点。缩点后变成一棵k个点k-1条割边连接成的树。而割点可以存在于多个块中。 8.双连通分量:分为点双连通和边双连通。它的标准定义为:点连通度大于1的图称为点双连通图,边连通度大于1的图称为边双连通图。通俗地讲,满足任意两点之间,能通过两条或两条以上没有任何重复边的路到达的图称为双连通图。无向图G的极大双连通子图称为双连通分量。 Tarjan算法的应用论述: 1.求强连通分量、割点、桥、缩点: 对于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求有向图强连通分量、割点、割边的代码: Var n,m,i,j,x,y,z:longint; a,b:array[0..1000,0..1000]of longint;//图 dfn,low,s:array[0..1000]of longint;//dfn为时间戳,low为祖先,s为栈 vis,ins:array[0..1000]of boolean;//vis为是否访问,ins为是否在栈中 num,p:longint; function min(x,y:longint):longint; begin if x

强连通分量个数的最小值

强连通分量个数的最小值 1. 引言 在图论中,强连通分量是指图中的一组顶点,其中任意两个顶点都存在一条有向路径。强连通分量个数的最小值是指在一个有向图中,最少需要将多少个顶点组成一个强连通分量。本文将介绍强连通分量的概念、计算方法以及如何求解强连通分量个数的最小值。 2. 强连通分量的定义 在有向图中,如果从顶点A到顶点B存在一条有向路径,同时从顶点B到顶点A也存在一条有向路径,则称顶点A和顶点B是强连通的。如果一个有向图中的每个顶点都与其他所有顶点强连通,则该有向图被称为强连通图。而强连通分量则是指有向图中的一组顶点,其中任意两个顶点都是强连通的,且不与其他顶点强连通。 3. 强连通分量的计算方法 为了计算一个有向图的强连通分量,可以使用强连通分量算法,其中最常用的是Tarjan算法和Kosaraju算法。 3.1 Tarjan算法 Tarjan算法是一种深度优先搜索算法,用于寻找有向图的强连通分量。算法的基本思想是通过DFS遍历图中的每个顶点,并记录每个顶点的遍历次序和能够到达的最小顶点次序。通过这些信息,可以判断顶点是否属于同一个强连通分量。 具体步骤如下: 1.初始化一个空栈和一个空的遍历次序数组。 2.对于每个未遍历的顶点,进行深度优先搜索。 3.搜索过程中,记录每个顶点的遍历次序和能够到达的最小顶点次序,并将顶 点加入栈中。 4.当搜索完成后,根据遍历次序和能够到达的最小顶点次序,可以确定每个顶 点所属的强连通分量。 3.2 Kosaraju算法 Kosaraju算法是另一种用于计算有向图强连通分量的算法。算法的基本思想是通过两次深度优先搜索来确定强连通分量。 具体步骤如下: 1.对原始图进行一次深度优先搜索,记录顶点的遍历次序。

tarjan算法的原理

tarjan算法的原理 Tarjan算法原理及应用 一、引言 Tarjan算法是一种用于图的深度优先搜索的算法,它可以在无向图或有向图中找到所有强连通分量。这个算法由美国计算机科学家Robert Tarjan于1972年提出,被广泛应用于图论和算法领域。本文将介绍Tarjan算法的原理及其应用。 二、Tarjan算法原理 1. 深度优先搜索 Tarjan算法是基于深度优先搜索的,深度优先搜索是一种图遍历算法,从一个顶点出发,沿着一条路径一直往下走,直到不能再走为止,然后回溯到前一个顶点,继续向未走过的路径探索。这种搜索方式可以用递归或栈来实现。 2. 强连通分量 在图中,如果任意两个顶点之间都存在路径,那么它们构成一个强连通分量。强连通分量是图中的一个重要概念,它可以帮助我们理解图结构的特性。 3. Tarjan算法步骤 Tarjan算法通过深度优先搜索来寻找强连通分量,其具体步骤如下:(1)初始化。将所有顶点标记为未访问状态,定义一个栈来保存已

经访问的顶点。 (2)深度优先搜索。从图中的任意一个未访问的顶点开始进行深度优先搜索。 (3)标记顶点。在搜索过程中,对每个顶点进行标记,记录其访问顺序(也称为时间戳)和能够到达的最小时间戳。 (4)寻找强连通分量。当一个顶点的访问顺序等于能够到达的最小时间戳时,说明它是一个强连通分量的根节点。通过弹出栈中的顶点,可以找到该强连通分量中的所有顶点。 三、Tarjan算法应用 Tarjan算法在图论和算法设计中有着广泛的应用,下面介绍几个常见的应用场景: 1. 强连通分量的查找 Tarjan算法可以高效地找到图中的所有强连通分量。这对于解决一些实际问题非常有用,比如社交网络中的群组划分、电路中的等价关系判断等。 2. 有向图的可达性分析 在有向图中,Tarjan算法可以用来判断两个顶点之间是否存在路径。这对于解决一些路径相关的问题非常有帮助,比如寻找关键路径、判断死锁等。 3. 编译器优化

图的连通性检测方法

图的连通性检测方法 图论是数学的一个分支,研究图形结构以及图形之间的关系。在图 论中,连通性是一个重要的概念,用于描述图中的节点或顶点之间是 否存在路径相连。连通性检测方法是用来确定一个图是否是连通图的 方法。本文将介绍几种常用的图的连通性检测方法。 一、深度优先搜索(DFS) 深度优先搜索是一种常用的图遍历算法,也可以用来检测图的连通性。该方法从图中的一个顶点开始,沿着一条路径尽可能深的搜索, 直到到达无法继续搜索的节点,然后回溯到上一个节点,继续搜索其 他路径。具体步骤如下: 1. 选择一个起始节点作为根节点。 2. 遍历该节点的邻接节点,并标记为已访问。 3. 递归的访问未访问过的邻接节点,直到所有节点都被访问过。 4. 如果所有节点都被访问过,则图是连通的;否则,图是不连通的。 DFS算法的时间复杂度为O(V+E),其中V是节点数,E是边数。 二、广度优先搜索(BFS) 广度优先搜索也是一种常用的图遍历算法,同样可以用来检测图的 连通性。该方法从图中的一个顶点开始,先访问其所有邻接节点,然 后再依次访问它们的邻接节点。具体步骤如下:

1. 选择一个起始节点作为根节点。 2. 将该节点加入一个队列中。 3. 从队列中取出一个节点,并标记为已访问。 4. 遍历该节点的邻接节点,将未访问过的节点加入队列中。 5. 重复步骤3和步骤4,直到队列为空。 6. 如果所有节点都被访问过,则图是连通的;否则,图是不连通的。 BFS算法的时间复杂度同样为O(V+E)。 三、并查集 并查集是一种数据结构,常用于解决图的连通性问题。它可以高效 地合并集合和判断元素是否属于同一个集合。具体步骤如下: 1. 初始化并查集,每个节点都是一个独立的集合。 2. 遍历图中的每条边,将边的两个节点合并到同一个集合中。 3. 判断图是否连通的方法是查找两个节点是否属于同一个集合。 并查集的时间复杂度为O(V+E)。 四、最小生成树 最小生成树是指一个连通图的生成树,其所有边的权值之和最小。 如果一个图是连通的,那么它的最小生成树就是它本身。因此,可以

Tarjan算法

[有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components)。 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达。{5},{6}也分别是两个强连通分量。 直接根据定义,用双向遍历取交集的方法求强连通分量,时间复杂度为 O(N^2+M)。更好的方法是Kosaraju算法或Tarjan算法,两者的时间复杂度都是O(N+M)。本文介绍的是Tarjan算法。 [Tarjan算法] Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。 定义DFN(u)为节点u搜索的次序编号(时间戳),Low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号。由定义可以得出, Low(u)=Min{DFN(u),Low(v),(u,v)为树枝边,u为v的父节点 DFN(v),(u,v)为指向栈中节点的后向边(非横叉边)} 当DFN(u)=Low(u)时,以u为根的搜索子树上所有节点是一个强连通分量。 算法伪代码如下

tarjan(u) { DFN[u]=Low[u]=++Index // 为节点u设定次序编号和Low初值 Stack.push(u)// 将节点u压入栈中 for each (u, v) in E // 枚举每一条边 if(v is not visted)// 如果节点v未被访问过 tarjan(v)// 继续向下找 Low[u]= min(Low[u], Low[v]) else if(v in S)// 如果节点u还在栈内 Low[u]= min(Low[u], DFN[v]) if(DFN[u]== Low[u])// 如果节点u是强连通分量的根 repeat v = S.pop// 将v退栈,为该强连通分量中一个顶点 print v until (u== v) } 接下来是对算法流程的演示。 从节点1开始DFS,把遍历到的节点加入栈中。搜索到节点u=6时, DFN[6]=LOW[6],找到了一个强连通分量。退栈到u=v为止,{6}为一个强连通分量。 返回节点5,发现DFN[5]=LOW[5],退栈后{5}为一个强连通分量。

tarjan LCA 算法

tarjan LCA 算法 2009-03-26 02:14 LCA是求最近公共祖先问题, tarjan的算法是离线算法,时间复杂度为O(n+Q),n为数据规模,Q为询问个数 其中用到并查集。关键是dfs的主循环比较重要。离线算法就是对每个查询,都要求以下,此算法在lrj的黑书中简单提起过,后边还有O(n)-o(1)的算法,正在研究中。。。 分类,使每个结点都落到某个类中,到时候只要执行集合查询,就可以知道结点的LCA了。 对于一个结点u,类别有以u为根的子树、除类一以外的以f(u)为根的子树、除前两类以外的以f(f(u))为根的子树、除前三类以外的以f(f(f(u)))为根的子树…… 类一的LCA为u,类二为f(u),类三为f(f(u)),类四为f(f(f(u)))。这样的分类看起来好像并不困难。但关键是查询是二维的,并没有一个确定的u。接下来就是这个算法的巧妙之处了。 利用递归的LCA过程。当lca(u)执行完毕后,以u为根的子树已经全部并为了一个集合。而一个lca的内部实际上做了的事就是对其子结点,依此调用lca.当v1(第一个子结点)被lca,正在处理v2的时候,以v1为根的子树+u同在一个集合里,f(u)+编号比u小的u的兄弟的子树同在一个集合里,f(f(u)) + 编号比f(u)小的f(u)的兄弟的子树同在一个集合里……而这些集合,对于v2的LCA都是不同的。因此只要查询x在哪一个集合里,就能知道 LCA(v2,x) 还有一种可能,x不在任何集合里。当他是v2的儿子,v3,v4等子树或编号比u大的u的兄弟的子树(等等)时,就会发生这种情况。即还没有被处理。还没有处理过的怎么办?把一个查询(x1,x2)往查询列表里添加两次,一次添加到x1的列表里,一次添加到x2的列表里,如果在做x1的时候发现 x2已经被处理了,那就接受这个询问。(两次中必定只有一次询问被接受) 其他介绍: 首先,Tarjan算法是一种离线算法,也就是说,它要首先读入所有的询问(求一次LCA叫做一次询问),然后并不一定按照原来的顺序处理这些询问。而打乱这个顺序正是这个算法的巧妙之处。看完下文,你便会发现,如果偏要按原来的顺序处理询问,Tarjan算法将无法进行。Tarjan算法是利用并查集来实现的。它按DFS的顺序遍历整棵树。对于每个结点x,它进行以下几步操作: * 计算当前结点的层号lv[x],并在并查集中建立仅包含x结点的集合,即 root[x]:=x。 * 依次处理与该结点关联的询问。 * 递归处理x的所有孩子。 * root[x]:=root[father[x]](对于根结点来说,它的父结点可以任选一个,反正这是最后一步操作了)。

2-sat问题的tarjan算法

2-sat问题是一种布尔可满足性问题,即判断一个由布尔变量和它们的逻辑运算构成的合取范式是否存在可满足的赋值。在计算机科学和逻 辑学中,2-sat问题具有重要的理论和实际意义。为了解决2-sat问题,人们提出了许多有效的算法,其中tarjan算法是一种经典且高效的解 决方法。 1. tarjan算法的概述 tarjan算法是由美国的计算机科学家Robert Tarjan在1972年提出的,它主要用于解决有向图中的强连通分量问题。在2-sat问题中,可以 将布尔变量和它们的逻辑运算构成的合取范式转化为一个有向图。然 后利用tarjan算法来求解图中的强连通分量,从而判断2-sat问题是 否可满足。 2. tarjan算法的原理 tarjan算法的核心是利用深度优先搜索(DFS)来遍历图中的节点, 并且通过维护一个栈来记录搜索路径上的节点。在DFS的过程中,通 过比较节点的深度和搜索路径上的节点的深度来判断是否存在环路, 从而找到强连通分量。利用tarjan算法求解2-sat问题的关键在于将 逻辑运算转化为有向图,同时构建出正确的搜索路径和深度信息,以 便进行强连通分量的判断。

3. tarjan算法的优势 与其他算法相比,tarjan算法具有许多优势。tarjan算法的时间复杂 度为O(V+E),其中V为图中的节点数,E为图中的边数。这意味着即使在大规模的图中,tarjan算法也能够在合理的时间内得到结果。tarjan算法的实现相对比较简单,只需要进行一次DFS遍历和一些基 本的数据结构操作即可完成。另外,tarjan算法的结果也比较容易理 解和解释,对于2-sat问题的求解具有很好的可解释性。 4. tarjan算法的应用 由于tarjan算法在解决2-sat问题中具有较高的效率和可靠性,因此 它在实际的计算机科学和工程领域得到了广泛的应用。在编译原理中,可以利用tarjan算法进行程序的静态分析和优化;在人工智能和图像 处理中,可以利用tarjan算法对逻辑规则进行推理和推导;在电路设 计和布线规划中,也可以利用tarjan算法对逻辑电路进行布线和优化。可以说,tarjan算法不仅仅是一种理论上的解决方法,更是一种具有 实用价值的工程工具。 5. 结语 tarjan算法作为解决2-sat问题的一种经典算法,具有较高的理论价 值和实际应用意义。它的高效性、可解释性和简单实现性使得它在计

tarjan与kosaraju算法合集

Kosaraju算法 Kosaraju算法基于以下思想:强连通分量一定是某种DFS形成的DFS树森林。Kosaraju 算法给出了更具体的方式: ①任意进行一次DFS,记录下每个节点的结束时间戳f[i]。 ②按f[i]的大小对节点进行排序(从小到大)。 ③以②的排序结果为顺序再进行一次DFS,所得的DFS树森林即为强连通分量。这次DFS 可以用Floodfill进行,把每个强连通分量标上不同序号。 (这就是OI界传说中的“求强连两遍DFS的算法”) 比如上图,我们可以得到: d[1]=1 f[1]=14 d[2]=2 f[2]=5 d[3]=6 f[3]=13 d[4]=3 f[4]=4 d[5]=7 f[5]=8 d[6]=9 f[6]=12 d[7]=10 f[7]=11 根据f[i]排序得:④②⑤⑦⑥③①(发现了什么?就是后序遍历),再按照这个顺序进行DFS 即可。 程序: var a:array[1..1000,1..1000]of longint; b,flag:array[1..1000]of longint; n,i,j,m,k:longint; procedure dfs(x:longint); var j:longint; begin flag[x]:=1; for j:=1 to n do if (flag[j]=0)and(a[x,j]>0) then dfs(j); inc(m); b[m]:=x; end; procedure fill(x,k:longint); var j:longint; begin flag[x]:=k; for j:=n downto 1 do if (flag[b[j]]=0)and(a[b[j],x]>0) then fill(b[j],k); end; begin readln(n); for i:=1 to n do

强连通分量的三种算法

有向图中, u可达v不一定意味着v可达u. 相互可达则属于同一个强连通分量(S trongly Connected Component, SCC) 最关键通用部分:强连通分量一定是图的深搜树的一个子树。 一、Kosaraju算法 1. 算法思路 基本思路: 这个算法可以说是最容易理解,最通用的算法,其比较关键的部分是同时应用了原图G和反图GT。(步骤1)先用对原图G进行深搜形成森林(树),(步骤2)然后任选一棵树对其进行深搜(注意这次深搜节点A能往子节点B走的要求是EAB存在于反图GT),能遍历到的顶点就是一个强连通分量。余下部分和原来的森林一起组成一个新的森林,继续步骤2直到没有顶点为止。 改进思路: 当然,基本思路实现起来是比较麻烦的(因为步骤2每次对一棵树进行深搜时,可能深搜到其他树上去,这是不允许的,强连通分量只能存在单棵树中(由开篇第一句话可知)),我们当然不这么做,我们可以巧妙的选择第二深搜选择的树的顺序,使其不可能深搜到其他树上去。想象一下,如果步骤2是从森林里选择树,那么哪个树是不连通(对于GT来说)到其他树上的呢?就是最后遍历出来的树,它的根节点在步骤1的遍历中离开时间最晚,而且可知它也是该树中离开时间最晚的那个节点。这给我们提供了很好的选择,在第一次深搜遍历时,记录时间i离开的顶点j,即numb[i] =j。那么,我们每次只需找到没有找过的顶点中具有最晚离开时间的顶点直接深搜(对于GT来说)就可以了。每次深搜都得到一个强连通分量。 隐藏性质: 分析到这里,我们已经知道怎么求强连通分量了。但是,大家有没有注意到我们在第二次深搜选择树的顺序有一个特点呢?如果在看上述思路的时候,你的脑子在思考,相信你已经知道了!!!它就是:如果我们把求出来的每个强连通分量收缩成一个点,并且用求出每个强连通分量的顺序来标记收缩后的节点,那么这个顺序其实就是强连通分量收缩成点后形成的有向无环图的拓扑序列。为什么呢?首先,应该明确搜索后的图一定是有向无环图呢?废话,如果还有环,那么环上的顶点对应的所有原来图上的顶点构成一个强连通分量,而不是构成环上那么多点对应的独自的强连通分量了。然后就是为什么是拓扑序列,我们在改进分析的时候,不是先选的树不会连通到其他树上(对于反图GT来说),也就是后选的树没有连通到先选的树,也即先出现的强连通分量收缩的点只能指向后出现的强连通分量收缩的点。那么拓扑序列不是理所当然的吗?这就是Kosaraju算法的一个隐藏性质。 2. 伪代码 Kosaraju_Algorithm: step1:对原图G进行深度优先遍历,记录每个节点的离开时间。

最近公共祖先LCA(C++版)

LCA 最近公共祖先 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先,就是两个节点在这棵树上深度最大的公共的祖先节点。 换句话说,就是两个点在这棵树上距离最近的公共祖先节点。 所以LCA主要是用来处理当两个点仅有唯一一条确定的最短路径 时的路径。 有人可能会问:那他本身或者其父亲节点是否可以作为祖先节点 呢? 答案是肯定的,很简单,按照人的亲戚观念来说,你的父亲也是你 的祖先,而LCA还可以将自己视为祖先节点。 举个例子吧,如下图所示4和5的最近公共祖先是2,5和3的最 近公共祖先是1,2和1的最近公共祖先是1。 这就是最近公共祖先的基本概念了,那么我们该如何去求这个最近 公共祖先呢? 通常初学者都会想到最简单粗暴的一个办法:对于每个询问,遍历所有的点,时间复杂度为O(n*q),很明显,n和q一般不会很小。 常用的求LCA的算法有:Tarjan/DFS+ST/倍增 后两个算法都是在线算法,也很相似,时间复杂度在O(logn)~O(nlogn)之间。 什么是Tarjan(离线(塔尔杨))算法呢?顾名思义,就是在一次遍历中把所有询问一次性解决,所以其时间复杂度是O(n+q)。 Tarjan算法的优点在于相对稳定,时间复杂度也比较居中,也很容易理解。 下面详细介绍一下Tarjan算法的基本思路: 1.任选一个点为根节点,从根节点开始。 2.遍历该点u所有子节点v,并标记这些子节点v已被访问过。 3.若是v还有子节点,返回2,否则下一步。 4.合并v到u上。 5.寻找与当前点u有询问关系的点v。 6.若是v已经被访问过了,则可以确认u和v的最近公共祖先为v被合并到的父亲节点a。 遍历的话需要用到dfs来遍历(我相信来看的人都懂吧...),至于合并,最优化的方式就是利用并查集来合并两个节点。 下面上伪代码: 1 Tarjan(u)//marge和find为并查集合并函数和查找函数 2 { 3for each(u,v) //访问所有u子节点v 4{ 5Tarjan(v); //继续往下遍历 6marge(u,v); //合并v到u上 7标记v被访问过; 8} 9for each(u,e) //访问所有和u有询问关系的e 10{ 11如果e被访问过; 12u,e的最近公共祖先为find(e); 13} 14 }

Tarjan's SCC算法

Tarjan's strongly connected components algorithm Tarjan's Algorithm(named for its discoverer, Robert Tarjan) is a graph theory algorithm for finding the strongly connected components of a graph. Although it precedes it chronologically, it can be seen as an improved version of Kosaraju's algorithm, and is comparable in efficiency to Gabow's algorithm. Contents[hide] ? 1 Idea ? 2 The root property ? 3 The algorithm in pseudocode ? 4 Remarks ? 5 References ? 6 External links Idea The basic idea of the algorithm is this: a depth-first search begins from a start node. The strongly connected components form the subtrees of the search tree, the roots of which are the roots of the strongly connected components. The nodes are placed on a stack in the order in which they are visited. When the search returns from a subtree, the nodes are taken from the stack and it is determined whether each node is the root of a strongly connected component{How to determine?}. If a node is the root of a strongly connected component, then it and all of the nodes taken off before it form that strongly connected component. The root property The crux of the algorithm comes in determining whether a node is the root of a strongly connected component. To do this, each node is given a depth search index v.index, which numbers the nodes consecutively in the order in which they are discovered.

数据结构课设有向图强连通分量求解

数据结构课设有向图强连通分量求解 强连通分量(Strongly Connected Components)是有向图中的一种特殊的连通分量,指的是图中任意两个顶点之间都存在有向路径。 求解有向图的强连通分量可以使用Tarjan算法,该算法基于深度优先搜索(DFS)。 具体步骤如下: 1. 初始化一个栈,用于存储已访问的顶点; 2. 对于图中的每个顶点v,如果v未访问,则进行DFS遍历; 3. 在DFS遍历中,对于当前访问的顶点v,设置v的索引号和低链接号为当前索引值,并将v入栈; 4. 遍历v的所有邻接顶点w,如果w未访问,则进行DFS遍历,并将w的低链接号更新为min(当前顶点的低链接号, w的低链接号); 5. 如果当前顶点v的低链接号等于索引号,则将v出栈,并将v及其出栈的顶点组成一个强连通分量。 示例代码如下: ```python class Graph: def __init__(self, vertices): self.V = vertices self.adj = [[] for _ in range(vertices)] self.index = 0

self.lowlink = [float("inf")] * vertices self.onStack = [False] * vertices self.stack = [] self.scc = [] def addEdge(self, u, v): self.adj[u].append(v) def tarjanSCC(self, v): self.index += 1 self.lowlink[v] = self.index self.stack.append(v) self.onStack[v] = True for w in self.adj[v]: if self.lowlink[w] == float("inf"): self.tarjanSCC(w) self.lowlink[v] = min(self.lowlink[v], self.lowlink[w]) elif self.onStack[w]: self.lowlink[v] = min(self.lowlink[v], self.lowlink[w]) if self.lowlink[v] == self.index: scc = [] w = -1 while w != v:

c++中tarjan算法求割点

c++中tarjan算法求割点 (原创实用版) 目录 1.介绍 Tarjan 算法 2.割点的概念和求解方法 3.C++中实现 Tarjan 算法求割点的示例代码 4.总结 正文 一、介绍 Tarjan 算法 Tarjan 算法是一种用于解决图的割点问题的算法,由 Robert C.Tarjan 于 1973 年提出。它是一种基于深度优先搜索(DFS)的算法,可以高效地找到图的割点。割点是指在一个有向无环图中,当移除该点后,图被分成了两个不连通的部分。 二、割点的概念和求解方法 在有向无环图中,割点是一个重要的概念。求解割点的目的是找到这样一个点,当该点被移除后,图被分成了两个不连通的部分。求解割点的方法有很多,其中 Tarjan 算法是一种比较常见的方法。 Tarjan 算法的基本思想是:从某个起始点开始,通过深度优先搜索遍历整个图,同时对每个点进行处理。处理过程包括:找到该点的所有邻接点,对邻接点进行处理,以及判断当前点是否为割点。 三、C++中实现 Tarjan 算法求割点的示例代码 以下是一个 C++中实现 Tarjan 算法求割点的示例代码: ```cpp #include

#include #include using namespace std; const int maxn = 1e5 + 5; int fa[maxn], dep[maxn], siz[maxn], son[maxn], top[maxn], dfn[maxn], low[maxn], tp[maxn]; void tarjan(int u, int father, int depth) { fa[u] = father; dep[u] = depth; siz[u] = 1; for (int v : son[u]) { if (v == father) continue; if (!dfn[v]) { tarjan(v, u, depth + 1); siz[u] += siz[v]; if (siz[son[u]] < siz[v]) swap(son[u], v); } else { low[u] = min(low[u], dep[v]); dfn[v] = depth + 1; } } if (siz[son[u]] == siz[u] - 1 && low[u] == depth) tp[u] = 1;

c++中tarjan算法求割点

C++中 Tarjan 算法求割点 一、概述 Tarjan 算法是一种用于图论中寻找割点和桥的经典算法。在C++ 中,我们可以利用 Tarjan 算法来高效地求解图中的割点问题,本文将详细介绍 Tarjan 算法在 C++ 中的实现方法。 二、什么是割点? 在图论中,割点又被称为关节点或者关节,指的是在图中去掉该点以 及与该点相连的边后,原图被分为多个连通分量。割点可以用于判断 图的连通性,以及在网络中寻找连接关键节点的路径。 三、Tarjan 算法原理 Tarjan 算法是一种通过深度优先搜索(DFS)来寻找图中割点的算法。其基本原理是利用 DFS 遍历图,并利用节点的深度和祖先节点的信息来判断是否是割点。 四、C++ 实现 我们需要包含一些必要的头文件: ```cpp #include #include

#include using namespace std; ``` 我们定义一个常量表示最大节点数: ```cpp const int MAXN = 1005; ``` 定义一个结构体表示图的边: ```cpp struct Edge { int to, next; }; ``` 接下来,我们定义一个全局变量数组表示图的邻接表和一些辅助数组: ```cpp int head[MAXN], dfn[MAXN], low[MAXN], vis[MAXN]; int dfs_clock, ans;

vector edges; ``` 在主函数中,我们可以实现 Tarjan 算法的主要逻辑: ```cpp void tarjan(int u, int fa) { dfn[u] = low[u] = ++dfs_clock; int child = 0; for (int i = head[u]; i != -1; i = edges[i].next) { int v = edges[i].to; if (!dfn[v]) { child++; tarjan(v, u); low[u] = min(low[u], low[v]); if (low[v] >= dfn[u]) { if (u != fa || child > 1) { ans++; } } } else if (dfn[v] < dfn[u] v != fa) { low[u] = min(low[u], dfn[v]); }

c++中tarjan算法求割点

c++中tarjan算法求割点 【原创版】 目录 1.算法背景 2.TARJAN 算法原理 3.TARJAN 算法具体实现 4.应用案例 5.总结 正文 一、算法背景 在计算机图形学中,割点(Cutting Point)是指一个图形在裁剪过程中,使得内部节点处于内部,而不会被裁剪到的点。在 C++中,TARJAN 算法是一种求割点的经典算法,其时间复杂度为 O(n),空间复杂度为 O(n),其中 n 为节点数。 二、TARJAN 算法原理 TARJAN 算法的核心思想是基于深度优先搜索(DFS)和低高标记(Low-High 标记)的方法。具体来说,算法分为以下几个步骤: 1.对每个节点进行 DFS 搜索,同时记录当前节点的父节点。 2.对于搜索过程中发现的每个环,找出环的入口节点,将其标记为割点。 3.完成 DFS 搜索后,对所有割点进行处理,如连接割点与对应节点等。 三、TARJAN 算法具体实现 以下是 TARJAN 算法的具体实现步骤:

1.初始化所有节点的父节点为自身,将所有节点标记为未访问。 2.遍历所有节点,对每个节点进行 DFS 搜索。 a.如果当前节点未被访问,则将其标记为已访问,并记录当前节点的父节点。 b.如果当前节点已被访问,则判断当前节点是否为环的入口节点。如果是,将其标记为割点。 3.完成 DFS 搜索后,遍历所有割点,对割点进行处理。 四、应用案例 TARJAN 算法在计算机图形学中有广泛应用,例如在求解裁剪问题、计算几何中交点等方面。通过使用 TARJAN 算法,可以有效地提高算法的效率。 五、总结 TARJAN 算法是一种求割点的经典算法,其时间复杂度和空间复杂度都较低。

相关主题
相关文档
最新文档