我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Kasaraju算法--强连通图遍历及其

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Kasaraju算法--强连通图遍历及其

在理解有向图和强连通分量前必须理解与其对应的两个概念,连通图(无向图)和连通分量。

连通图的定义是:如果一个图中的任何一个节点可以到达其他节点,那么它就是连通的。

例如以下图形:

这是最简单的一个连通图,即使它并不闭合。由于节点间的路径是没有方向的,符合从任意一个节点出发,都可以到达其他剩余的节点这一条件,那么它就是连通图了。

 

连通分量

 

显然这也是一个图,只不过是由三个子图组成而已,但这并非一个连通图。这三个子图叫做这个图的连通分量,连通分量的内部归根还是一个连通图。

 

有向图:

在连通图的基础上增加了方向,两个节点之间的路径只能有单一的方向,即要么从节点A连向节点B,要么从节点B连向节点A。有向图与连通图(更准确来说是无向图)最大的区别在于节点之间的路径是否有方向。

有向图也分两种,一种是有环路的有向图。另外一种是无环路的有向图,即通常所说的有向无环图DAG(Directed Acyclic Graph)。严格来说,第一种有环路的图,如果任意一个节点都可以与其他节点形成环路,那么它也是一个连通图。

 

例如下面的就为一个有向图同时也是连通图:

 

强连通分量

强连通分量SCCs(strongly connected components)是一种有向的连通图。

如果一个图的连通分量是它里面所有节点到能够彼此到达的最大子图,那么强连通分量SCCs就是一个有向图中所有节点能够彼此到达的子图。

 

显然由345组成的子图是无法到达由012组成的子图的。那么012和345分别组成两个强连通分量。

 

在实际的现实问题中,我们考虑问题可能就不会简单地研究无向图。例如地图上的最短路径规划,ARP路由算法等等,考虑的都是有向图的问题。

如果有这样一个需求,我们希望用最少的次数遍历所有节点,怎么处理呢?

 

时间效应问题,强连通分量间的时间问题。

如果有向图的各个强连通分量中的元素个数相仿,那么,它们内部分别进行遍历的时间量级别是相等的,但实际情况是,这种情况很少发生。一般从一个强连通分量到另一个强连通分量。

 

正如上面的需求:如何用最少的次数遍历整个有向图的所有节点。假设我们将0、1、2组成子图1,将3、4、5组成子图,子图1有一条指向子图2的路径。这时候,我们从子图1的任意一点开始遍历。假设我们从1开始遍历,那么遍历的顺序将会是1—2,那么来到2的时候问题来了,是先走0的路径还是走子图1和子图2之间的路径去遍历节点3呢?

如果我们先遍历节点0,那么我们遍历完节点0之后,发现节点1已经遍历过,就会返回节点2,再沿着子图1和子图2之间的路径去遍历子图2。这看起来是挺合理的。

但问题是,如果是先遍历节点3(也就是说先遍历子图2)呢?

假设沿着子图1和子图2的路径去遍历子图2,那么子图2遍历完后,子图1还剩下节点0没有被遍历,这时候就会出现很为难的事情,因为之前遍历的情况无法判断哪些节点是没有遍历的,只能是原路返回,依次去从新遍历,“发现”哪些节点是还没去遍历的。似乎上图比较简单,这种方法不会耗费太多的时间。但如果是节点2连接着(并指向)许多个强连通子图的有向图,这种“返回式”的遍历将会是很费劲的一件事。

 

为了解决这个问题,Kosaraju算法提出了它的解决方案。Kosaraju算法的核心操作是将所有节点间的路径都翻转过来。下面分析一下为什么这种算法有它的优势。

还是拿上面的图来讲述。想象一下上面的有向图中的所有节点间的路径都翻转过来了。读者可以自己用一张纸简单画一下。就像下面的图:

 

这一次,我们还是以0、1、2组成子图1,以3、4、5组成子图2。所不同的是,这次遍历的起始点从子图1开始。

 

多强连通分量的有向图

再来看一下这个多子图的强连通图,如果像上图所示,从子图1开始,就会像上文提到的那样,遍历到节点2,会出现多个去向的问题。而在还没有遍历完子图1的前提下,从节点2过渡到子图2/子图3,再回溯的时候会引来较大的麻烦。通过Kosaraju算法之后,从2节点出发的路径都会变成指向2。此时,遍历的起点还是从子图1开始,由于子图1没有出路,就不会出现上面所说的问题。再遍历完子图1后,继续遍历子图2、子图3。而子图2、子图3的遍历都是在强连通分量内部实现的。

 

 

算法实现

邻接集表示的有向图

N={
    "a":{"b"},   #a
    "b":{"c"},   #b
    "c":{"a","d","g"},   #c
    "d":{"e"},   #d
    "e":{"f"},   #e
    "f":{"d"},   #f
    "g":{"h"},   #g
    "h":{"i"},   #h
    "i":{"g"}    #i
}

 

翻转图实现代码:

def re_tr(G):
    GT = {}
    for u in G:
        for v in G[u]:
            # print(GT)
            if GT.get(v):
                GT[v].add(u)
            else:
                GT[v] = set()
                GT[v].add(u)

    return GT

 

深度遍历算法实现代码:

#递归实现深度优先排序
def rec_dfs(G,s,S=None):
    if S is None:
        #S = set()    #集合存储已经遍历过的节点
        S = list()    #用列表可以更方便查看遍历的次序,而用集合可以方便用difference求差集
    # S.add(s)
    S.append(s)
    print(S)
    for u in G[s]:
        if u in S:continue
        rec_dfs(G,u,S)

return S

 

在强连通图内遍历

#遍历有向图的强连通分量
def walk(G,start,S=set()):     #传入的参数S,即上面的seen很关键,这避免了通过连通图之间的路径进行遍历
    P,Q = dict(),set()      #list存放遍历顺序,set存放已经遍历过的节点     
    P[start] = None
    Q.add(start)
    while Q:
        u = Q.pop()                      #选择下一个遍历节点(随机性)
        for v in G[u].difference(P,S):         #返回差集
            Q.add(v)
            P[v] = u
    print(P)    
    return P

 

获得强连通分量

#获得各个强连通图
def scc(G):
    GT = re_tr(G)
    sccs,seen = [],set()
    for u in rec_dfs(G,"a"):    #以a为起点
        if u in seen:continue
        C = walk(GT,u,seen)
        seen.update(C)
        sccs.append(C)
    return sccs

 

单元测试

print(scc(N))

结果:
{'a': None, 'c': 'a', 'b': 'c'}
{'d': None, 'f': 'd', 'e': 'f'}
{'g': None, 'i': 'g', 'h': 'i'}
[{'a': None, 'c': 'a', 'b': 'c'}, {'d': None, 'f': 'd', 'e': 'f'}, {'g': None, 'i': 'g', 'h': 'i'}]

 

这是本人学习过程所写的第一篇关于图的算法文章,供大家一起学习讨论。其中难免会有错误。如有错误之处,请各位指出,万分感谢!

 

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Kasaraju算法--强连通图遍历及其

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Kasaraju算法--强连通图遍历及其

在理解有向图和强连通分量前必须理解与其对应的两个概念,连通图(无向图)和连通分量。连通图的定义是:如果一个图中的任何一个节点可以到达其他节点,那么它就是连通的。例如以下图形:这是最简单的一个连通图,即使它并不闭合。由于节点间的路径是没有方向
2023-01-30

JavaMorris遍历算法及其在二叉树中的应用

Morris遍历是一种基于线索二叉树的遍历算法,可以在不使用栈或递归的情况下,实现二叉树的前序、中序和后序遍历。该算法利用二叉树中的空指针或线索指针,将遍历序列嵌入到原二叉树中,实现了常数级别的空间复杂度,适用于对空间要求较高的场景
2023-05-18

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录