Tarjan 算法

Tarjan算法详解(AcWing 1174 受欢迎的牛) 题目描述: 每一头牛的愿望就是变成一头最受欢迎的牛。 现在有 N 头牛,编号从 1 到 N,给你 M 对整数 (A,B),表示牛 A 认为牛 B 受欢迎。 这种关系是具有传递性的,如果 A 认为 B 受欢迎,B 认为 C 受欢迎,那么牛 A 也认为牛 C 受欢迎。 你的任务是求出有多少头牛被除自己之外的所有牛认为是受欢迎的。 输入格式 第一行两个数 N,M; 接下来 M 行,每行两个数 A,B,意思是 A 认为 B 是受欢迎的(给出的信息有可能重复,即有可能出现多个 A,B)。 输出格式 输出被除自己之外的 阅读详情

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

三.算法模板

复制代码

 1 void Tarjan ( int x ) {
 2          dfn[ x ] = ++dfs_num ;
 3          low[ x ] = dfs_num ;
 4          vis [ x ] = true ;//是否在栈中
 5          stack [ ++top ] = x ;
 6          for ( int i=head[ x ] ; i!=0 ; i=e[i].next ){
 7                   int temp = e[ i ].to ;
 8                   if ( !dfn[ temp ] ){
 9                            Tarjan ( temp ) ;
10                            low[ x ] = gmin ( low[ x ] , low[ temp ] ) ;
11                  }
12                  else if ( vis[ temp ])low[ x ] = gmin ( low[ x ] , dfn[ temp ] ) ;
13          }
14          if ( dfn[ x ]==low[ x ] ) {//构成强连通分量
15                   vis[ x ] = false ;
16                   color[ x ] = ++col_num ;//染色
17                   while ( stack[ top ] != x ) {//清空
18                            color [stack[ top ]] = col_num ;
19                            vis [ stack[ top-- ] ] = false ;
20                  }
21                  top -- ;
22          }
23 }

复制代码

 

注:本文章上部分内容转载自http://www.cppblog.com/sosi/archive/2010/09/26/127797.html;一方面是网上有很多关于tarjan算法的介绍,我觉得都没有这个他的文章介绍的简明易懂或者没有具体的实现。另一方面,自己也顺便用java实现了一下,所以发表出来和大家分享分享!

 

[有向图强连通分量]

在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components)。

下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达。{5},{6}也分别是两个强连通分量。

 

[Tarjan算法]

Tarjan算法是基于对图深度优

定义DFN(u)D记录搜索到该u的时间,也就是第几个搜索u的。Low(u)为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)            // 如果节点v还在栈内

            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}为一个强连通分量。

返回节点3,继续搜索到节点4,把4加入堆栈。发现节点4向节点1有后向边,节点1还在栈中,所以LOW[4]=1。节点6已经出栈,(4,6)是横叉边,返回3,(3,4)为树枝边,所以LOW[3]=LOW[4]=1。

继续回到节点1,最后访问节点2。访问边(2,4),4还在栈中,所以LOW[2]=DFN[4]=5。返回1后,发现DFN[1]=LOW[1],把栈中节点全部取出,组成一个连通分量{1,3,4,2}。

 

至此,算法结束。经过该算法,求出了图中全部的三个强连通分量{1,3,4,2},{5},{6}。

可以发现,运行Tarjan算法的过程中,每个顶点都被访问了一次,且只进出了一次堆栈,每条边也只被访问了一次,所以该算法的时间复杂度为O(N+M)。

算法java实现如下:

Tarjan类:

 


 
  1. import java.util.ArrayList;

  2. import java.util.Arrays;

  3. import java.util.List;

  4. import java.util.Stack;

  5.  
  6.  
  7. public class Tarjan {

  8. private int numOfNode;

  9. private List< ArrayList<Integer> > graph;//图

  10. private List< ArrayList<Integer> > result;//保存极大强连通图

  11. private boolean[] inStack;//节点是否在栈内,因为在stack中寻找一个节点不方便。这种方式查找快

  12. private Stack<Integer> stack;

  13. private int[] dfn;

  14. private int[] low;

  15. private int time;//

  16.  
  17. public Tarjan(List< ArrayList<Integer> > graph,int numOfNode){

  18. this.graph = graph;

  19. this.numOfNode = numOfNode;

  20. this.inStack = new boolean[numOfNode];

  21. this.stack = new Stack<Integer>();

  22. dfn = new int[numOfNode];

  23. low = new int[numOfNode];

  24. Arrays.fill(dfn, -1);//将dfn所有元素都置为-1,其中dfn[i]=-1代表i还有没被访问过。

  25. Arrays.fill(low, -1);

  26. result = new ArrayList<ArrayList<Integer>>();

  27. }

  28.  
  29. public List< ArrayList<Integer> > run(){

  30. for(int i=0;i<numOfNode;i++){

  31. if(dfn[i]==-1){

  32. tarjan(i);

  33. }

  34. }

  35. return result;

  36. }

  37.  
  38. public void tarjan(int current){

  39. dfn[current]=low[current]=time++;

  40. inStack[current]=true;

  41. stack.push(current);

  42.  
  43. for(int i=0;i<graph.get(current).size();i++){

  44. int next = graph.get(current).get(i);

  45. if(dfn[next]==-1){//-1代表没有被访问

  46. tarjan(next);

  47. low[current]=Math.min(low[current], low[next]);

  48. }else if(inStack[next]){

  49. low[current]=Math.min(low[current], dfn[next]);

  50. }

  51. }

  52.  
  53. if(low[current]==dfn[current]){

  54. ArrayList<Integer> temp =new ArrayList<Integer>();

  55. int j = -1;

  56. while(current!=j){

  57. j = stack.pop();

  58. inStack[j]=false;

  59. temp.add(j);

  60. }

  61. result.add(temp);

  62. }

  63. }

  64.  
  65. }

  66.  
  67.  

测试类:

 

 


 
  1. import java.util.ArrayList;

  2. import java.util.List;

  3. import java.util.Stack;

  4.  
  5.  
  6. public class Main {

  7. public static void main(String[] args) {

  8. //创建图

  9. int numOfNode = 6;

  10. List< ArrayList<Integer> > graph = new ArrayList<ArrayList<Integer>>();

  11. for(int i=0;i<numOfNode;i++){

  12. graph.add(new ArrayList<Integer>());

  13. }

  14. graph.get(0).add(1);

  15. graph.get(0).add(2);

  16. graph.get(1).add(3);

  17. graph.get(2).add(3);

  18. graph.get(2).add(4);

  19. graph.get(3).add(0);

  20. graph.get(3).add(5);

  21. graph.get(4).add(5);

  22. //调用Tarjan算法求极大连通子图

  23. Tarjan t = new Tarjan(graph, numOfNode);

  24. List< ArrayList<Integer> > result = t.run();

  25. //打印结果

  26. for(int i=0;i<result.size();i++){

  27. for(int j=0;j<result.get(i).size();j++){

  28. System.out.print(result.get(i).get(j)+" ");

  29. }

  30. System.out.println();

  31. }

  32.  
  33. }

  34. }

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

Tarjan算法详解 目录一、什么是强连通分量1. 概念2. 强连通分量二、两种dfs遍历1. 方式12. 方式2三、一个简单例子理解算法四、更完整的一个例子五、Code实现 一、什么是强连通分量 tarjan强连通分量算法 1. 概念 连通:无向图中,从任意点i可到达任一点j 强连通:有向图中,从任意点i可到达任一点j 弱连通:把有向图看作无向图时,从任意点i可到达任一点j 如图,强连通无论那个点,都能按照方向到达任意一点,弱连通如果强行按方向,那么B到不了C,A到不了B和C,C到不了B。但如果把他看作是无向图,那么他们也 阅读详情

相关推荐

【图论】Tarjan算法详解

在学习Tarjan算法之前,要先了解强连通的相关知识点! 强连通: 在一个有向图G里,如果有两个点(a、b)可以相互到达,我们就叫这两个顶点(a,b)为强连通。 强连通图: 如果在一个有向图G中,每两个点都强连通(可以相互到达),我们就叫这个图为强连通图。 强连通分量(SCC): 在一个有向图G中,有一个子图,这个子图每2个点都满足强连通,我们就叫这个子图叫做 强连通分量,孤立的...

360°顺滑 2947

tarjan算法伪代码说明

【代码】targn算法伪代码说明。

qq_42637799的博客 231

tarjan算法原理介绍

证明比较繁琐,仔细检查了应该没有大错,记录一下证明过程。 强连通分量定义: 在有向图中,强连通分量的定义是:有向图的某个子图,其中任意两个点之间可以互达。 强连通分量的定理: 定理1:一个完整的强连通分量一定包含在一棵深度优先搜索树中。 定理2:子图是强连通分量子图中的每一条路径都归属于一个环状(除非只有一个点)。 证明:根据强连通分量的定义,任意两个点之间可以互达,所以等价于

zakheav的专栏 5739

有向图的强连通分量

有向图的强连通分量 『StronglyConnectedComponent』(SCC) 一、Kosaraju算法 1、伪代码 Kosaraju_Algorithm: step1:对原图G进行深度优先遍历,记录每个节点的离开时间。 step2:选择具有最晚离开时间的顶点,对反图GT进行遍历,删除能够遍历到的顶点,这些顶点构成一个强连通分量。 step3:如果还有顶点没...

aiechu6301的博客 220

第八篇 永磁同步电机控制-MTPA、MTPV

MTPA 的全称是 Maximum Torque Per Ampere,即 最大转矩电流比控制。核心思想:在输出特定转矩时,通过控制定子电流的幅值和相位(即d轴和q轴电流的分量),使得所需要的定子电流幅值最小。通俗理解:就是用“最省电”的方式产生所需要的力矩。就像开车时,找到一个最合适的油门和档位配合,使得在输出相同动力时,油耗最低。

深耕汽车电子:AUTOSAR、电机控制,助力数字汽车 1871

ICCompiler II学习笔记(一)ICC II使用全流程 overview

学习笔记同步发布在我的个人网站上,欢迎来访查看。 个人学识有限,理解可能有误,仅供参考。 文章目录一、前言二、 ICC II 简介和 GUI2.1 Blocks 和 Design Libraries2.2 Objects2.3 Application Options三、Design Setup四、APR flow五、 课程主题:如何用 ICC II来实现最好的PPA 一、前言 现在的芯片都在追求一个极致的PPA 芯片的生成可以分为以下 5大步骤: Front-end Design:前端设计,最终产生

一名FPGA芯片设计爱好者的技术博客 2万+

Tarjan 算法(超详细!!)

之前太多错误了,重构了

WerChange的OI博客 7285

磁栅尺的基本原理

一、磁栅尺简介 磁栅尺(Magnetic scale)是一种常见于工业自动化设备领域的线性位移测量方案,因为其耐用性强,对环境要求不高,逐渐替代了对环境要求苛刻的光学直线测量方案。 作为自动化设备领域里不多见的精密位移检测方案,磁栅尺使用广泛,尤其适用于检测行程长、测量精度要求高、工况复杂的大型机床。磁栅尺安装方便,抗干扰能力强,抗冲击性强,不易受到振动、粉尘、水垢、油污等影响,耐高温和耐低温性能也较为突出。 二、磁栅尺基本组成与原理 磁栅尺主要由两个部分组成,磁尺和读磁头。 磁尺 磁尺是磁栅尺

Luckboy_shy的博客 1万+

Tarjan算法详细讲解

Tarjan算法讲解的博客网上找到三篇比较好的,现在都转载了,个人只研究了第一篇,正如博主所说,讲的标比较详细,清晰,剩下两篇也可以看一下. 卿学姐视频讲解https://www.bilibili.com/video/av7330663/ 以下内容转自:http://www.cnblogs.com/uncle-lu/p/5876729.html 全网最详细tarjan算法讲解,我不敢说别的。反正其他tarjan算法讲解,我看了半天才看懂。我写的这个,读完一遍,发现原来tarjan这么简单! ...

木牛的博客 1万+

点云目标检测:open3d多窗口联动可视化

多个模型之间的对比除了指标之外,还需要可以直接直观的对比可视化结果,这次介绍一下如何使用open3d同时打开两个窗口分别加载两个模型的结果进行对比,同时实现两个窗口的联动,也就是当一个窗口拖动或者缩放时另外一个窗口也跟着做同样的变换。

zhaoguanhua的博客 4688

手把手教您如何在飞牛NAS上部署OpenClaw(Clawdbot/Moltbot)智能助手,接入钉钉,0成本使用(万字保姆级教程)

想在飞牛NAS上部署一个24小时待命的AI助手?🤖 本文手把手教您如何在飞牛NAS上部署一个完全免费的OpenClaw智能助手 ✅

XiaoqiangClub的博客 7230

Tarjan算法

## **Tarjan算法** ## Tarjan算法是一种求图的强连通分量的一种算法。首先我们介绍一些基本知识。

千古的博客 4592

反应动力学仿真软件:Kintecus_(4).用户界面及操作指南

在 Kintecus 中,反应方程的定义非常灵活。您可以定义简单的一阶反应、复杂的多步反应以及不同类型的反应机制。

kkchenjj的博客 1182

tarjan算法_Tarjan 这个算法大神

有同学在学习图论算法的时候,发现这里有个 Tarjan 算法,那里有个 Tarjan 算法,而似乎 Tarjan 算法解决的问题并不一样,于是非常迷惑:Tarjan 算法到底是指什么?这是一个很好的问题。Tarjan 是计算机领域的大牛,发明了很多现在大家耳熟能详的算法或者数据结构,所以有同学会觉得冠他名字的算法有些多。但如果我们仔细梳理一下,其实并不复杂。在这篇文章中,我会带领大家梳理...

weixin_39821378的博客 642

高通IPA硬件加速介绍

‌ 高通IPA (Internet Protocol Accelerator)模块是一种硬件加速模块,由高通公司(Qualcomm)提供,主要用于优化移动设备上的网络数据传输,特别是通过 LTE 、 5G 等移动网络的数据传输‌‌。IPA模块通过硬件加速,显著提高了数据传输的速度和效率,同时降低了设备的功耗,并提供了更稳定的网络连接,减少了数据传输中的丢包和延迟‌。

四儿家的小祖宗的博客 1623

Xilinx FPGA平台GTX简易使用教程(汇总篇)

Xilinx平台GTX简易使用教程,先“知其然”,慢慢再研究“所以然”。

数字IC/FPGA杂货铺,找一找,或许有你想要的~ 3万+

Tarjan算法_LCA

首先我们要知道什么是LCA、深度优先生成树以及并查集在计算LCA的算法中我们有离线算法以及在线算法两种,我们这里使用的Tarjan算法是使用的是离线算法,即将所有的问题一次性输入,然后再一次性输出答案本人感觉这个算法的写法和Tarjan写法还是有很大差距的,相同的地方大部分是在思想上我们首先来举个例子,模拟一下这个算法的实现过程假设这是我们要计算的树,我们要计算7--8,5--6,5--2,4--...

A_Bo的博客 7584

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

浅谈 Tarjan 算法

无向图中,若删去一条边会使得这个图的极大连通分量数增加,则该边被称为桥。也可以理解为无向图的一个连通块中,若删除一条边会使得至少两点之间无法相互到达,该边被称为桥。无向图中,若删去一个点会使得这个图的极大连通分量数增加,这个点被称为割点。也可以理解为无向图的一个连通块中,若删除一个点会使得至少两点之间无法相互到达,该点被称为割点。注意,强连通分量(SCC,Strongly Connected Components)是在单向图中的。强联通子图,定义为:在单向图中,该子图上的任意两点之间能互相到达。

William_Wang_的博客 5220

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

Tarjan:这个算法大神

摘要:图的算法是进行静态分析的基础数据算法,如何提高图的分析效率,就需要对图的算法有进一步的认识。 1. 引言 在静态分析技术中, 我们常用会将代码转成抽象语法树(AST), 然后采用深度遍历(DFS)来完成对语法树的遍历和查询,找到潜在的问题缺陷。 对于语义的分析,我们采用的控制流和数据流也都无一例外的采用了以图为基础的算法, 通过图的可达性, 来完成变量、表达式的可达分析, 以及变量的依赖分析、值流图等等。 图的算法是进行静态分析的基础数据算法,如何提高图的分析效率,就需要对图的算法有进一步的认

华为云官方博客 2109

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

上一篇: FAT 32 文件系统学习
下一篇: PPTP协议详解及报文解析
废言Pro
博客等级 码龄16年 1093粉丝 142原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值