Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >客户端基本不用的算法系列:从 floodfill 到图的连通性

客户端基本不用的算法系列:从 floodfill 到图的连通性

作者头像
用户2932962
发布于 2019-07-05 02:35:12
发布于 2019-07-05 02:35:12
1.3K00
代码可运行
举报
文章被收录于专栏:程序员维他命程序员维他命
运行总次数:0
代码可运行

满足欧拉回路的一个大前提是判断当前图是一个连通图。问题又随之而来,什么是连通图?如何才能判断一个图到底是不是连通图?带着这个问题来看后面的内容。

话题引子

先给大家描述一下场景:笔者家乡在天津的大港油田,在油田作业区中会有钻探团队负责检测地下的石油储量。很多块临近的具有石油储量的区域将会在一起工作,所以通过网格来划分整片区域,并将作业区划成了多个作业块。在这种情况下,需要统计作业块的总个数从而预估作业成本。

我们将问题简单的抽象一下,将最大的作业区抽象成一个 m*n 的字符矩阵, *代表没有石油的无用之地, @代表具有石油储量的地方。例如如下矩阵:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
****@*@@*@*@**@@@@*@@@**@

如上图所示的描述矩阵,我们可以给其划分成两个作业块:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            @    @@         @ @    And   @@@@         @@@          @

判断一个点周围是否有其他点与其组成一个作业块,只需要找到当前格子的周围 8 个点(强调一下,斜线也考虑到情况中)。那其实我们的思路就很清晰了,只需要遍历一遍所有的矩阵区域,发现第一个 @就开始对图用不同的标记染色,最后对染色计数就完成了统计。Talk is cheap,看代码。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
g = [    '****@',    '*@@*@',    '*@**@',    '@@@*@',    '@@**@',]
idx = [[0 for _ in range(0, len(g[0]))] for i in range(0, len(g))]
def dfs(x: int, y: int, color: int):    if x < 0 or x >= len(g) or y < 0 or y >= len(g[0]):        return    if idx[x][y] > 0 or g[x][y] != '@':        return    idx[x][y] = color    for dx in range(-1, 2, 1):        for dy in range(-1, 2, 1):            if dx != 0 or dy != 0:                dfs(x=x + dx, y=y + dy, color=color)
col = 1for i in range(0, len(g)):    for j in range(0, len(g[0])):        if idx[i][j] == 0 and g[i][j] == '@':            dfs(x=i, y=j, color=col)            col += 1
print("区域数量", col - 1)

如上代码使用一个双层循环来遍历一个点周围的 8 个点,然后再递归 8 个点的情况继续相同 color 的染色,从而达到区域内中的一点染色,使得整片区域染色。可以继续延伸思考下这个题目,其实使用 BFS 也可以将所有区块扫出来,因为染色问题只要标记就可以在让下一步完成状态更新。

回归文章的目的,我们为什么要出这道题呢?其实这道题就是经典的 Floodfill 算法,Floodfill 的原型是从一个区域中提取若干个连通的点与其他相邻区域区分开(或分别染成不同颜色)的算法,它的临近只是包含了上下左右四个方向,而这个题目又增加了斜对角的四个方向,但是搜索的思想是一样的。

或许你会想,这道题和图论有什么关系?其实,坐标图也是图的一种,我们可以理解成每一个 @ 在周围的 8 个方向上,如果存在另一个 @ 就说明它有一条边是连接彼此的。我们这样就将所有的 @ 节点组织到一张图中,并且由于分成多个作业块,所以这张图在 col 大于 1 的情况下,这张图是不连通的。我们引出图连通的定义:

图连通:如果无向图 G 中的任意两个节点联通,则称图 G 是联通的。

连通分量:如果无向图 G 是非连通的,那么每一个天然分隔的子图都是父亲图的联通分量。

我们从建图的角度来看,具有 8 个方向临近关系的节点其实就是加了一条边,而我们要求解的结果其实就是父亲图的联通分量的个数。(或许还可以尝试一下并查集?)

图的术语

继续用油田作业区的例子,这里我构建一个真正的图来表示作业区:

如果我们有真正的两个作业块,并且我们在 DG 两个节点进行加边操作,使其连接,那么这个图就变成了通过 DG 边连接的联通图。

在这张大图中, DG 因为是唯一联通两个联通分量的边,所以称 DG桥(bridge)

单独拿出 D 这个节点来看,如果我们去掉 D 以及与它直接相连的所有度,则图又会变成具有两个联通分量的非连通图。所以说 D 节点是整张图的割点(cut point)

于是我们引入以下几个定义:

割点:无向连通图中,去掉一个顶点及和它相邻的所有边,图中的连通分量数增加,则该顶点称为割点。

桥(又叫割边):无向联通图中,去掉一条边,图中的连通分量数增加,则这条边,称为桥或者割边。

看到这里,又会想当然的以为:~~两个割点之间的边一定是桥、割边的两个端点一定是割点~~。切记,这是错的!画两个图自己体会:

另外其他还有一些概念,我也一并列出,后面的所有场景可能会出现这些定义名称:

割点集合:在一个无向连通图中,如果有一个顶点集合 V,删除顶点集合 V 以及与 V 中顶点相连(至少有一端在 V 中)的所有边后,原图不连通,就称点集 V 为割点集合

点连通度:最小割点集合中的顶点数

割边集合:如果有一个边集合,删除这个边集合后,原图不连通,就称这个边集为割边集合。

边连通度:最小割边集合中的边数。

双连通:如果一个无向连通图的边连通度大于 1,则称该图是边双连通的(edge biconnected),也称双连通或重连通。

有了这些关于无向图的定义(包括上一篇文章的欧拉路欧拉图等),如果将对应场景的题目抽象建图,就可以解决更多的问题。

再来一道,找感觉

学了很多的理论定义,我们还是需要落回到 Coding 上,这里再给出一道可以多解的习题,大家下来也可以利用其他方法来尝试能否解出。

题目:给出 N 个单词,判断是否这些单词可以“接龙”。所谓的“接龙”和我们日常玩的成语接龙是一个意思,例如:worlddead 这两个单词,因为 world 的结尾是 d,并且 dead 的开头也是 d,所以这两个单词便可“接龙”。如果这一组单词可以接成一条龙,我们就认为给出的单词集是合法的。

我们首先来分析问题,其实我们并不是很关注单词中间具体是什么,而是关心每个单词的首尾字母分别是啥。这两个字母代表了出入的关系。我们换一个角度来讲,如果我们将 26 个字母当作 26 个图节点,那么这些单词的定义其实就是一组边关系。例如 world 这个单词,其实就是给 wd 这两个字母节点增加了一个有向边。

如此建图之后,你就会发现,这其实是我们之前的七桥问题(一笔画问题),只要通过单词集加边后形成的图是一个欧拉图,就可以完成“接龙”。当输入单词集后,我们要证明生成的图是一个欧拉图,那么就要证明这个图是满足两个条件的:

  1. 图是连通的,即是一个连通图;
  2. 对于有向欧拉图来说,需要统计每个点的入度和出度满足:最多有两个点满足出度和入度数量不等,且其中一点出度比入度大1,另一点入度比出度大1

确定思路,我们来写代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import copy
words = ["world", "dead", "deadline", "export", "tiger", "range"]
# 临界矩阵g = [[0 for _ in range(0, 26)] for _ in range(0, 26)]
# 标记数组,图染色使用vis = [0 for _ in range(0, 26)]
# 出度和入度统计inp, outp = [0 for _ in range(0, 26)], [0 for _ in range(0, 26)]
# 字母表chrtoint = {}
for dn in range(0, 26):    base_a = ord("a")    chrtoint[chr(base_a + dn)] = dn
def dfs(u: int):    """  图联通判断  使用图染色法,将所有与当前节点连接的点染色标记  """    vis[u] = 0    for v in range(0, 26):        if vis[v] == 1 and g[u][v] == 1:            dfs(v)
# 初始化邻接矩阵for ind, txt in enumerate(words):    inp[chrtoint[txt[0]]] += 1    outp[chrtoint[txt[-1]]] += 1    vis[chrtoint[txt[0]]] = 1    vis[chrtoint[txt[-1]]] = 1    g[chrtoint[txt[0]]][chrtoint[txt[-1]]] = 1
import numpy as npfrom collections import Counter
# 看入度和出度的差de = list(np.array(inp) - np.array(outp))ce = Counter(de)if ce[-1] == 1 and ce[1] == 1 and len(ce) <= 3:        # 查找起点,即 入度 - 出度 = 1 的点    start = [i for i, n in enumerate(de) if n == 1][0]        # 连通性测试    dfs(u=start)    vsc = Counter(vis)    if 0 in vsc.keys() and len(vsc.keys()) == 1:        print("可以接龙")    else:        print("无法接龙")else:    print("这个图不是欧拉图")

通过对欧拉图以及连通性的学习,我们可以用图论知识来解决上述看上去像字符串的题目。其实,图论关注的都是节点和节点之间的关系,一旦发现可以建图,并且可以嵌套图论中的算法模型,你会发现很多问题都是很有套路的。后面如果我还能坚持写到二分图,你会发现算法并不难,难的是建图

提炼重点

在上面的单词接龙中,我们需要掌握的是有向图如果处理欧拉图的问题。当我们判断一个图是否是欧拉图,需要先判断其连通性,再确定是否满足欧拉图的必要条件。

判断连通性,通过 DFS 图染色就可以解决,是不是很像我们的 Floodfill 算法?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def dfs(u: int):    vis[u] = 0    for v in range(0, 26):        if vis[v] == 1 and g[u][v] == 1:            dfs(v)

判断欧拉路,其实就是简单的出度和入度的计数法,在 Python 中可以简单的使用 Counter 这个类来轻松构建计数字典,并且通过 numpy 中的矩阵相减来轻松解决出入度的相等问题。用 Python 的列表推导轻松的搜到起点(Python 是不是写起来很爽?? )。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import numpy as npfrom collections import Counter
# 看入度和出度的差de = list(np.array(inp) - np.array(outp))ce = Counter(de)
# ....start = [i for i, n in enumerate(de) if n == 1][0]
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-07-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员维他命 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
客户端基本不用的算法系列:Tarjan 算法的思路
在之前的 《客户端基本不用的算法系列:从 floodfill 到图的连通性》一文中,我们已经了解了在无向图中的割点和桥的定义。
用户2932962
2019/07/19
1.1K0
图的割点、桥和双连通分支的基本概念
回到正题,首先介绍下什么是图的边连通度和点连通度。一般来说,点连通度是指对应一个图G,对于所有点集U属于V(G),也就是V(G)的子集中,使得G-U要么是一个非连通图,要么就是一个平凡图(即仅包含一个独立点的图),其中最小的集合U的大小就是图G的点连通度,有时候也直接称为图的连通度。通俗点说,就是一个图G最少要去掉多少个点会变成非连通图或者平凡图。当然对于一个完全图来说Kn来说,它的连通度就是n-1。 同理,边连通度就是对于一个非平凡图G,至少去掉多少条边才能使得该图变成非连通图。我们的问题就是,对于任意一个图,如何求该图的连通度以及边连通度?这跟最大流问题有什么联系? 简单起见,我们先说如何求一个图的边连通度lamda(G)。(基于无向图考虑) 对于图G,设u,v是图G上的两个顶点,定义r(u,v)为删除最少的边,使得u到v之间没有通路。将图G转换成一个流网络H,u为源点,v是汇点,边容量均为1,那么显然r(u,v)就是流网络的最小割,根据(二)里的介绍,其等于流网络的最大流。 但是,目前为止我们还没解决完问题,因为显然我们要求的边连通度lamda(G)是所有的点对<u,v>对应的r(u,v)中最小的那个值。这样的话我们就必须遍历所有的点对,遍历的的复杂度为O(n*n)。这显然代价太高,而事实上,我们也不必遍历所有点对。
风骨散人Chiam
2020/10/28
1.6K0
图的割点、桥和双连通分支的基本概念
TarJan 算法求解有向连通图强连通分量
在有向图G中,如果两个 顶点间至少存在一条路径,称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components)。
RainMark
2019/09/10
1.9K0
TarJan 算法求解有向连通图强连通分量
图的连通性问题专题整理
这一篇博客继续以一些OJ上的题目为载体,对图的连通性专题进行整理一下。会陆续的更新。。。
全栈程序员站长
2022/07/13
4430
离散数学--连通性和矩阵
(1)这个运算包括了矩阵的运算,包括这个幂运算,关系的合成,关系的逆运算,求解幂集等等;
阑梦清川
2025/02/24
1700
离散数学--连通性和矩阵
客户端基本不用的算法系列:图论的开端-七桥问题
冬瓜一直在想着写一个系列来罗列一些在客户端开发中,根本无法用到的算法。但是在计算机科学中又是能独立出来的学科。这之中图论就是一大块。
用户2932962
2019/07/05
6830
DFS序和欧拉序的降维打击
如下树的 dfs 序就是[1,2,8,8,5,5,2,4,3,9,9,3,6,6,4,7,7,1]。
一枚大果壳
2023/11/24
3130
DFS序和欧拉序的降维打击
tarjan算法详解
tarjan算法讲解。 tarjan算法,一个关于 图的联通性的神奇算法。基于DFS算法,深度优先搜索一张有向图。!注意!是有向图。根据树,堆栈,打标记等种种神奇方法来完成剖析一个图的工作。而图的联通性,就是任督二脉通不通。。的问题。 了解tarjan算法之前你需要知道: 强连通,强连通图,强连通分量,解答树(解答树只是一种形式。了解即可) 强连通(strongly connected): 在一个有向图G里,设两个点 a b 发现,由a有一条路可以走到b,由b又有一条路可以走到a,我们就叫这两个顶点(a,
attack
2018/04/12
2K0
tarjan算法详解
数据结构 图
1-1 无向连通图至少有一个顶点的度为1 错误: 无向连通图考点: 1. 每条边连接两个顶点,所有顶点的度之和等于边数的2倍 2.记住两个特殊的无相连通图模型: A: B: 1-2 用邻接表法存储图
Kindear
2018/01/15
1.9K0
数据结构 图
Kosaraju算法、Tarjan算法分析及证明--强连通分量的线性算法
一、背景介绍 强连通分量是有向图中的一个子图,在该子图中,所有的节点都可以沿着某条路径访问其他节点。强连通性是一种非常重要的等价抽象,因为它满足 自反性:顶点V和它本身是强连通的 对称性:如果顶点V和顶点W是强连通的,那么顶点W和顶点V也是强连通的 传递性:如果V和W是强连通的,W和X是强连通的,那么V和X也是强连通的 强连通性可以用来描述一系列属性,如自然界中物种之间的捕食关系,互相捕食的物种可以看作等价的,在自然界能量传递中处于同一位置。 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,
老白
2018/03/19
2.8K0
Kosaraju算法、Tarjan算法分析及证明--强连通分量的线性算法
【数据结构】图解图论:度、路径、连通性,五大概念一网打尽
在上一篇中,我们初步认识了图的定义与分类。今天,我们将深入探讨图的核心概念: • 顶点的度(无向图与有向图的入度、出度) • 路径与回路(简单路径、简单回路、路径长度的计算) • 距离与连通性(连通图、强连通图的判断) • 子图与连通分量(生成子图、极大连通子图)
蒙奇D索隆
2025/04/02
2900
7.4 图的连通性问题
1、在对无向图进行遍历时,对于连通图,仅需从图中任一顶点出发,进行深度优先搜索或广度优先搜索,便可访问到图中所有顶点。
小林C语言
2020/12/12
1.2K0
7.4 图的连通性问题
数据结构之图论(续)
在一个无向图G中,若将某个节点v去除之后后G所包含的连通域增多,则v称作切割节点(cut vertex或关节点(articulation point)。如果一个图不含任何关节点则称之为双连通图,最典型的就是完全图。任一无向图都可视作由若干个极大的双连 通子图组合而成,这样的每一子图都称作原图的一个双连通域(bi-connected component)。例如下图中的节点3和5就是关节点。
短短的路走走停停
2020/03/20
7010
图论基础,如何快速上手图论?
前面我们学过了一些基本的数据结构,像顺序表,链表,栈,队列,树等...其中最有难度的就属树的部分了,而图论的与树也是有关联的,在后续我们经常可以看到一些图类似树,但是又不是树,他们的区别在哪?二叉树是父亲节点和孩子的关联,是从上到下的,而图没有父亲节点和孩子节点,他主要使用节点描述各个事件之间的关系;
啊QQQQQ
2025/01/20
1340
图论基础,如何快速上手图论?
深度优先生成树及其应用
在上一篇博客判断有向图是否有圈中从递归的角度简单感性的介绍了如何修改深度优先搜索来判断一个有向图是否有圈。事实上, 它的实质是利用了深度优先生成树(depth-first spanning tree)的性质。那么什么是深度优先生成树?顾名思义,这颗树由深度优先搜索而生成的,由于无向图与有向图的深度优先生成树有差别,下面将分别介绍。 一. 无向图的深度优先生成树 无向图的深度优先生成树的生成步骤: 深度优先搜索第一个被访问的顶点为该树的根结点。 对于顶点v,其相邻的边w如果未被访问,则边(v, w)为该树的树
llhthinker
2018/01/24
2.2K0
图的基本概念以及DFS与BFS算法
顶点和边:图中结点称为顶点,第 i 个顶点记作 vi。两个顶点 vi 和 vj 相关联称作顶点 vi 和顶点 vj 之间有一条边,图中的第 k 条边记作 ek,ek = (vi,vj) 或 <vi,vj>。
利刃大大
2023/04/12
6790
图的基本概念以及DFS与BFS算法
数据结构与算法-图
图G是由集合V和E组成,记成 G =(V,E)。其中:V为顶点集,不可为空;E为边集,可为空。边是顶点的有序对或无序对,它反映了两顶点之间的关系。
越陌度阡
2020/11/26
5990
数据结构与算法-图
离散数学--图论
(1)这个里面的完全图比较重要,完全图是例如k3,k5这样的表示方法,角标表示的就是图上面的节点的个数;
阑梦清川
2025/02/24
1440
离散数学--图论
离散数学笔记第五章(图论 )
1.无向连通图 G 是欧拉图,当且仅当 G 不含奇数度结点( G 的所有结点度数为偶数); 2.无向连通图G 含有欧拉通路,当且仅当 G 有零个或两个奇数度的结点; 3.有向连通图 D 是欧拉图,当且仅当该图为连通图且 D 中每个结点的入度=出度; 4.有向连通图 D 含有欧拉通路,当且仅当该图为连通图且 D 中除两个结点外,其余每个结点的入度=出度,且此两点满足 deg-(u)-deg+(v)=±1 。(起始点s的入度=出度-1,结束点t的出度=入度-1 或两个点的入度=出度); 5.一个非平凡连通图是欧拉图当且仅当它的每条边属于奇数个环; 6.如果图G是欧拉图且 H = G-uv,则 H 有奇数个 u,v-迹仅在最后访问 v ;同时,在这一序列的 u,v-迹中,不是路径的迹的条数是偶数。 弗勒里算法 弗勒里(B.H.Fleury) 在1883 年给出了在欧拉图中找出一个欧拉环游的多项式时间算法,称为弗勒里算法(Fleury’salgorithm)。这个算法具体表述如下: 输入:一个连通偶图 G 和 G 中任意一个指定项点 u 输出:从 u 出发的 G 的一个欧拉环游 1、令 W:=u,x:=u,F:=G 2、while 3、选一条 中的边 e,其中 e 不是 F 的一条割边;如果 中的边都是割边,那么任选一条边 e 4、用 替换 ,用 y 替换 x ,用 替换 F 5、end while 6、返回 W 其算法核心就是沿着一条迹往下寻找,先选择非割边,除非这个点的邻边都是割边。这样得到一条新的迹,然后再继续往下寻找,直到把所有边找完。遵循这样一个原则就可以找出图的一个欧拉环游来。 在有向图中也可以类似地定义有向环游、有向欧拉环游、有向欧拉图和有向欧拉迹的概念。 类似地,有如下定理:一个有向图是有向欧拉图当且仅当这个图中每个顶点的出度和入度相等。 [1]
废江_小江
2022/09/05
9500
离散数学笔记第五章(图论 )
数据结构-图结构
图Graph是由顶点(图中的节点被称为图的顶点)的非空有限集合V与边的集合E(顶点之间的关系)构成的。 若图G中的每一条边都没有方向,则称G为无向图。 若图G中的每一条边都有方向,则称G为有向图。
WuShF
2023/07/08
4350
数据结构-图结构
相关推荐
客户端基本不用的算法系列:Tarjan 算法的思路
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验