tarjan算法详解

https://blog.csdn.net/jeryjeryjery/article/details/52829142?locationNum=4&fps=1

以防链接失效,特此转载此博,如有侵权请见谅

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

       Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。Tarjan算法有点类似于基于后序的深度遍历搜索和并查集的组合,充分利用回溯来解决问题。
在Tarjan算法中为每个节点i维护了以下几个变量:
DFN[i]:深度优先搜索遍历时节点i被搜索的次序。
low[i]:节点i能够回溯到的最早位于栈中的节点。
flag[i]:标记几点i是否在栈中。

Tarjan算法的运行过程:
(1).首先就是按照深度优先搜索算法搜索的次序对图中所有的节点进行搜索。
(2).在搜索过程中,对于任意节点u和与其相连的节点v,根据节点v是否在栈中来进行不同的操作:
*节点v不在栈中,即节点v还没有被访问过,则继续对v进行深度搜索。
*节点v已经在栈中,即已经被访问过,则判断节点v的DFN值和节点u的low值的大小来更新节点u的low值。如果节点v的 DFN值要小于节点u的low值,根据low值的定义(能够回溯到的最早的已经在栈中的节点),我们需要用DFN值来更新u 的low值。
(3).在回溯过程中,对于任意节点u用其子节点v(其实不能算是子节点,只是在深度遍历的过程中,v是在u之后紧挨着u的节点)的   low值来更新节点u的low值。因为节点v能够回溯到的已经在栈中的节点,节点u也一定能够回溯到。因为存在从u到v的直接路径,所以v能够到的节点u也一定能够到。

(4).对于一个连通图,我们很容易想到,在该连通图中有且仅有一个节点u的DFN值和low值相等。该节点一定是在深度遍历的过程中,该连通图中第一个被访问过的节点,因为它的DFN值和low值最小,不会被该连通图中的其他节点所影响。

      下面我们证明为什么仅有一个节点的DFN和low值相等。假设有两个节点的DFN值和low值相等,由于这两个节点的DFN值一定不相同 (DFN值的定义就是深度遍历时被访问的先后次序),所以两个的low值也绝对不相等。由于位于同一个连通图中,所以两个节点必定相互可达,那么两者的low值一定会被另外一个所影响(要看谁的low值更小),所以不可能存在两对DFN值和low值相等的节点。

       所以我们在回溯的过程中就能够通过判断节点的low值和DFN值是否相等来判断是否已经找到一个子连通图。由于该连通图中的DFN值和low值相等的节点是该连通图中第一个被访问到的节点,又根据栈的特性(先压入  栈的节点在栈的更里面),则该节点在最里面。所以能够通过不停的弹栈,直到弹出该DFN值和low值相同的节点来弹出该连通图中所有的节点。

 

Tarjan算法的C++实现代码如下,可以配合上面的图加以理解:

 

[cpp] view plaincopy
    1. #include<iostream>  
    2. using namespace std;  
    3. int DFN[105];                                  //记录在做dfs时节点的搜索次序  
    4. int low[105];                                  //记录节点能够找到的最先访问的祖先的记号  
    5. int count=1;                                   //标记访问次序,时间戳  
    6. int stack[105];                                //压入栈中  
    7. int top=-1;  
    8. int flag[105];                                 //标记节点是否已经在栈中  
    9. int number=0;  
    10. int j;  
    11. int matrix[105][105]={{0,1,1,0,0,0},{0,0,0,1,0,0},{0,0,0,1,1,0},{1,0,0,0,0,1},{0,0,0,0,0,1},{0,0,0,0,0,0}};  
    12. int length;                                    //图的长度  
    13. void tarjan(int u){  
    14.     DFN[u]=low[u]=count++;                     //初始化两个值,自己为能找到的最先访问的祖先  
    15.     stack[++top]=u;  
    16.     flag[u]=1;                                 //标记为已经在栈中  
    17.   
    18.     for(int v=0;v<length;v++){  
    19.     if(matrix[u][v]){  
    20.         if(!DFN[v]){                       //如果点i没有被访问过  
    21.         tarjan(v);                     //递归访问  
    22.         if(low[v]<low[u])  
    23.             low[u]=low[v];             //更新能找的到祖先  
    24.         }  
    25.         else{                              //如果访问过了,并且该点的DFN更小,则  
    26.         if(DFN[v]<low[u]&&flag[v])     //flag[v]这个判断条件很重要,这样可以避免已经确定在其他联通图的v,因为u到v的单向边而影响到u的low  
    27.         low[u]=DFN[v];                 //也就是已经确定了的联通图要剔除掉,剔除的办法就是判断其还在栈中,因为已经确定了的连通图的点  
    28.         }                                  //flag在下面的do while中已经设为0了(即已经从栈中剔除了)  
    29.     }  
    30.     }  
    31.   
    32.     //往后回溯的时候,如果发现DFN和low相同的节点,就可以把这个节点之后的节点全部弹栈,构成连通图  
    33.     if(DFN[u]==low[u]){  
    34.     number++;                               //记录连通图的数量  
    35.     do{  
    36.         j=stack[top--];                     //依次取出,直到u  
    37.         cout<<j<<" ";  
    38.         flag[j]=0;                          //设置为不在栈中  
    39.     }while(j!=u);  
    40.         cout<<endl;  
    41.     }  
    42. }  
    43. int main(){  
    44.       
    45.     memset(DFN,0,sizeof(DFN));                  //数据的初始化  
    46.     memset(low,0,sizeof(low));  
    47.     memset(flag,0,sizeof(flag));  
    48.       
    49.     length=6;  
    50.     tarjan(0);  
    51.   
    52.     cout<<endl;  
    53.     for(int i=0;i<6;i++){  
    54.     cout<<"DFN["<<i<<"]:"<<DFN[i]<<" low["<<i<<"]:"<<low[i]<<endl;  
    55.     }  
    56.     return 0;  
    57. }  

转载于:https://www.cnblogs.com/cglongge/p/8722202.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/280702.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Gitlab简单使用CI/CD

开篇语大概是去年就想做这个事情了&#xff0c;奈何当时卡到一个docker命令找不到的问题上&#xff0c;导致文章难产了&#xff0c;墨迹了这么久&#xff0c;终于又有空来捣鼓它了。目的我们要实现的目的是我本地不断提交代码(CI),然后服务器不断进行部署(CD)的一个简单流程。准…

免费的数字图书馆_不仅是书籍:您当地图书馆可能提供的所有免费数字资料

免费的数字图书馆You might think of libraries as old fashioned, or irrelevant in the age of the internet. You’d be wrong. 您可能会认为图书馆是老式的&#xff0c;或者与互联网时代无关。 你会错的。 Modern libraries offer books, yes, but they also provide inter…

iNeuOS工业互联网操作系统,脚本化实现设备运行时长和效率计算与统计

目 录1. 概述... 22. 实时采集开停状态... 23. 增加虚拟设备... 24. 脚本统计和计算设备运行时长... 45. 设备运行时长报表... 71. 概述有一个煤矿项目&#xff0c;使用iNeuOS系统时有一个需要是&#xff1a;要统计设备的运行时长&#xff0c…

C# 图片、文件等加入Project Resources

一、目的 1.编译后&#xff0c;只想有一个exe文件&#xff0c;不想外部文件引用&#xff0c;直接运行exe文件即可。 2.不会出现文件丢失情况。 二、操作 1.右击project ->properties->Resource&#xff0c;左上角选择Image&#xff08;或其他类型&#xff09; 2. 点击…

jfinal使用shiro注解大体流程

2019独角兽企业重金招聘Python工程师标准>>> 上一篇答题梳理了jfinal整合shiro的流程&#xff0c;jfinal读取shiro注解&#xff0c;这一篇将作为补充。 1.JFinalShiroPlugin作者为shiro的RequiresRoles&#xff0c;RequiresPermissions&#xff0c; RequiresAuthent…

chrome 快捷键取消_如何使用键盘快捷键在Chrome和Firefox中固定和取消固定选项卡...

chrome 快捷键取消If you tend to open a lot of tabs in your browser, it can become difficult to find the tabs with your most used websites. Pinning tabs in your browser moves those tabs to the left and shrinks the tabs to only show the favicon, and you can …

.NET Conf China 2022参会指南速览(内含超多福利)赶紧预约!⏰⏰⏰

12月充满惊喜各种美好节日纷至沓来似在奖励一年辛苦劳作的你12月的第一波福利.NET Conf China 承包啦立即扫码预约加入.NET年度盛宴抢12月第一波惊喜&#xff01;.NET Conf China 2022 .NET Conf China 2022是面向开发人员的社区峰会&#xff0c;延续 .NET Conf 2022 的活动&a…

​.Net 7 AOT 彻底解析下(完结篇)

楔子&#xff1a;本篇是承继前面三篇文章而来&#xff0c;分别为&#xff1a;.Net 7 的 AOT 和 CLR有什么区别&#xff1f;.Net 7 的 R2R,Crossgen2是什么?.Net 7 的AOT原理简析通过以上三篇的基础&#xff0c;本篇来彻底解析下AOT这门技术的底层原理。AOT此终&#xff0c;不再…

cmd暂停快捷键_是否有键盘快捷键可以暂停正在运行的CMD窗口的输出?

cmd暂停快捷键When running a batch script, you may need or want to pause the output in the CMD window so that you can look things over. Is there an easy way to pause, then restart the output? Today’s SuperUser Q&A post has the answer to help with a re…

在 Docker 中使用 flannel - 每天5分钟玩转 Docker 容器技术(60)

上一节我们安装和配置了 flannel&#xff0c;本节在 Docker 中使用 flannel。配置 Docker 连接 flannel编辑 host1 的 Docker 配置文件 /etc/systemd/system/docker.service&#xff0c;设置 --bip 和 --mtu。这两个参数的值必须与 /run/flannel/subnet.env 中 FLANNEL_SUBNET …

使用.NET7和C#11打造最快的序列化程序-以MemoryPack为例

译者注本文是一篇不可多得的好文&#xff0c;MemoryPack 的作者 neuecc 大佬通过本文解释了他是如何将序列化程序性能提升到极致的&#xff1b;其中从很多方面(可变长度、字符串、集合等)解释了一些性能优化的技巧&#xff0c;值得每一个开发人员学习&#xff0c;特别是框架的开…

永不丢失照片:防弹照片备份的完整指南

There’s nothing as precious and irreplaceable as your personal photos and, with a little forethought and planning, there’s no reason to ever feel the heartbreak of losing even a single one of them to theft, broken devices, or disaster. 没有比您的个人照片…

MySQL InnoDB存储引擎

呵呵哒。。。 MySQL体系结构和存储引擎 首先要搞懂的是什么是数据库&#xff0c;什么是数据库实例。 数据库&#xff1a;物理操作系统文件或其他形式文件类型的集合。 实例&#xff1a;MySQL数据库由后台线程以及一个共享内存区组成&#xff0c;实例才是真正对数据库进行操作的…

Blazor学习之旅 (8) MudBlazor组件库介绍

【Blazor】| 总结/Edison Zhou大家好&#xff0c;我是Edison。为了实现一个Web应用系统&#xff0c;需要有个看起来不丑的UI&#xff0c;而对于.NET程序员来说要做全栈开发还是有点难&#xff0c;而本篇介绍的这个UI组件库正好可以帮助我们解决这个问题&#xff01;MudBlaozr是…

棉花糖多少钱_如何在6.0棉花糖及更高版本中访问Android的正在运行的应用程序列表...

棉花糖多少钱In Android 5.x and below, accessing your list of running apps was simple—you’d jump into Settings > Apps > Running. Easy! In Android 6.0, however, Google moved this setting. It’s still not super difficult to find, but it’s a little tr…

C# 程序图标设置/winform 图标

一、目的、实际情况 1.编写一个winform 程序&#xff0c;发现有一个图标非常有意义。区分其他程序&#xff0c;以及感觉在做产品而不是写代码。 2.添加图标图片发现&#xff0c;需要用ico格式。在线转换&#xff08;某度搜索&#xff09;还是不靠谱。要微信登陆&#xff0c;登…

数字化转型,究竟在“转”什么?

这是头哥侃码的第265篇原创「头哥唠B唠」这个栏目已经持续了几个月了&#xff0c;没想到还在继续进行&#xff0c;并收获了很多朋友们的喜爱。非常感谢大家的支持&#xff01;在上次的直播中&#xff0c;我找来了我的老熟人们。一个是右军老师&#xff0c;之前 APISIX 的很多内…

Crash 的文明世界

题目描述 给一棵树&#xff0c;求以每个点为根时下列式子的值。 题解 当k1时这就是一个经典的换根dp问题。 所以这道题还是要用换根dp解决。 部分分做法&#xff1a; 考虑转移时是这样的一个形式(图是抄的)。 用二项式定理展开就可以nk2做了。 观察到结果是一个xk的形式。 然后…

wampServer配置WWW根目录遇到的坑

直接在官网下载之后开始安装&#xff0c;一切正常 打开使用&#xff0c;一切正常 设置WWW目录。坑了一波 按照的都是百度上的教程&#xff0c;设置httpd.conf 这里配置之后网页访问127.0.0.1 还是localhost都还是原始的www目录 后来 我发现了这里 是配置虚拟URL的地方。以上是正…

windows安装程序创建_如何在Windows上创建已安装程序的列表

windows安装程序创建Reinstalling Windows is a good way to fix serious problems with your computer, or just to get a fresh slate. But before you reinstall Windows, you should make a list of programs you currently have installed on your PC so you know what yo…