求图的连通子图 python 使用 networkx (BFS, DFS)

本来这个问题应该是放在并查集里面一起说明,不过并查集篇幅比较大,就单独把这个问题拿出来了。

并查集的问题也可以转化为图的连通子图问题。给一个图G,返回它的所有不连通的子图。

1. 使用networkx包求图的所有不连通的子图

主要使用connected_components()方法。下面是一个例子。

import networkx as nx
import matplotlib.pyplot as plt

pointList = ['A','B','C','D','E','F','G']
linkList = [('A','B'),('B','C'),('C','D'),('E','F'),('F','G'),]


def subgraph():
    G = nx.Graph()
    # 转化为图结构
    for node in pointList:
        G.add_node(node)

    for link in linkList:
        G.add_edge(link[0], link[1])

   # 画图
    plt.subplot(211)
    nx.draw_networkx(G, with_labels=True)
    color =['y','g']
    subplot = [223,224]
    # 打印连通子图
    for c in nx.connected_components(G):
       # 得到不连通的子集
        nodeSet = G.subgraph(c).nodes()
       # 绘制子图
        subgraph = G.subgraph(c)
        plt.subplot(subplot[0])  # 第二整行
        nx.draw_networkx(subgraph, with_labels=True,node_color=color[0])
        color.pop(0)
        subplot.pop(0)

    plt.show()
subgraph()

原图与分开后的子图


Screen Shot 2019-04-24 at 9.57.27 AM.png

那么networkx的connected_components()方法是如何实现的呢,也很简单,通过BFS遍历来找连通的子图。进行一次完整的BFS遍历可以一组连通节点,放入一个集合,然后从没有遍历到的节点再开始一次BFS遍历,放入集合就是另一组连通子图,以此类推可以几次BFS就找到几个连通子图,看源码:

seen = set()
    for v in G:
        if v not in seen:
            c = set(_plain_bfs(G, v))
            yield c
            seen.update(c)

2. 一个很经典的广度优先算法

networkx实现的BFS算法,这里使用G_adj[v]是邻接矩阵的存储方式,所以时间复杂度是O(|V|^2)

def _plain_bfs(G, source):
    """A fast BFS node generator"""
    G_adj = G.adj
    seen = set()
    nextlevel = {source}
    while nextlevel:
        thislevel = nextlevel
        nextlevel = set()
        for v in thislevel:
            if v not in seen:
                yield v
                seen.add(v)
                nextlevel.update(G_adj[v])

邻接表形式存储时,每个顶点均需搜索一次,时间复杂度T1=O(v),从一个顶点开始搜索时,开始搜索,访问未被访问过的节点。最坏的情况下,每个顶点至少访问一次,每条边至少访问1次,这是因为在搜索的过程中,若某结点向下搜索时,其子结点都访问过了,这时候就会回退,故时间复 杂度为O(E),算法总的时间复 度为O(|V|+|E|)
时间复杂度参考链接(https://blog.csdn.net/Charles_ke/article/details/82497543 )

3. 在这里顺便回顾一下深度优先遍历(DFS)

邻接矩阵表示时,查找每个顶点的邻接点所需时间为O(|V|),要查找整个矩阵,故总的时间复杂度为O(|V|^2)
邻接表表示时,查找所有顶点的邻接点所需时间为O(E),访问顶点的邻接点所花时间为O(V),此时,总的时间复杂度为O(V+E) (这里没自己实现邻接表代码,直接抄书上的过来了)

G的定义与上面相同
递归形式(参考王道数据结构):

G_adj = G.adj
seen = set()
def DFSTraverse(G):
    for v in G:
        if v not in seen:
            c = set(DFS(G,v))
            seen.update(c)

def DFS(G,v):
    seen.add(v)
    yield v
    # 访问操作
    print(v)
    for w in G_adj[v]:
        if w not in seen:
            DFS(G,w)

DFSTraverse(G)

使用队列实现DFS(参考链接https://blog.csdn.net/changyuanchn/article/details/79008760
):


def DFS2(G,node0):
    G_adj = G.adj
    #queue本质上是堆栈,用来存放需要进行遍历的数据
    #order里面存放的是具体的访问路径
    queue,order=[],[]
    #首先将初始遍历的节点放到queue中,表示将要从这个点开始遍历
    queue.append(node0)
    while queue:
        #从queue中pop出点v,然后从v点开始遍历了,所以可以将这个点pop出,然后将其放入order中
        #这里才是最有用的地方,pop()表示弹出栈顶,由于下面的for循环不断的访问子节点,并将子节点压入堆栈,
        #也就保证了每次的栈顶弹出的顺序是下面的节点
        v = queue.pop()
        order.append(v)
        #这里开始遍历v的子节点
        for w in G_adj[v]:
            #w既不属于queue也不属于order,意味着这个点没被访问过,所以讲起放到queue中,然后后续进行访问
            if w not in order and w not in queue:
                queue.append(w)
    return order

def DFSTraverse2(G):
    seenArray = []
    for v in G:
        if v not in seenArray:
            order = DFS2(G,v)
            seenArray.extend(order)
    return seenArray

seenArray = DFSTraverse2(G)
print(seenArray)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • 1. 图的定义和基本术语 线性结构中,元素仅有线性关系,每个元素只有一个直接前驱和直接后继;树形结构中,数据元素(...
    yinxmm阅读 5,376评论 0 3
  • 图是由顶点的有穷非空集合和顶点之间的边的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图中的顶点的集...
    keeeeeenon阅读 495评论 0 2
  • 1)这本书为什么值得看: Python语言描述,如果学的Python用这本书学数据结构更合适 2016年出版,内容...
    孙怀阔阅读 12,284评论 0 15
  • 很早就听过这部电影,在看了《声之形》后还是挡不住好奇心来看了。唯美的画风,动人的音乐,无逻辑漏洞的紧凑情节都促使我...
    西顾west阅读 204评论 0 1
  • 又是一年毕业季。今天,我们来聊聊年轻人的话题。 之前我们说过90后为什么这么穷,也说过为什么现在的年轻人很多都是月...
    Albert周阅读 174评论 0 0