简介
最近在学习一些图相关的算法时碰到一个比较有意思的问题。就是关于图中间桥的问题。在图中,一般的边是用于连接两个节点的。如果结合图的连通性来考虑,假设一个边连着图的两个部分,如果我们将这个边去掉,那么将使得图变成两个分割开的部分。那么,这个时候我们称这个边是桥。
那么,对于一个图来说,我们能否找到一些这样的桥呢?
分析
在找到具体的规律之前,我们先看一个图的示例:
从上图来看,我们需要找到一个边,这个边如果去掉之后会使得整个图被分割成两个不相连的部分。很明显,节点1和4构成的边恰好符合这个条件。而其他的边则不符合这个条件。那么这些不符合条件的边像<1, 2>, <2, 3> 为什么就删除了也不会破坏图的连通性呢?
很显然,因为这些边正好构成了一个连通的图,更精确的说,它们构成了一个环。对于一个环来说,每个节点是有两个连接的边,去掉一个对它的连通性不会有任何影响。因此,这就应该是问题的关键了。对于我们要讨论的问题来说,如果我们能够找到一个环上面所有的点,那么这些点之间的边就都可以排除成为bridge的可能性了。
现在的问题是,我们怎么来判断图中间存在环呢?就算我们判断出来了环,怎么把环上面的这些节点给标注出来? 首先一个,针对图中间环的判断,我们之前有文章进行过讨论。我们可以通过对图进行深度优先遍历,然后每次访问一个节点的时候就将该节点对应的一个标志位设置为true。在访问到某个已经之前访问过的节点,但不是当前节点的前一个节点时,我们可以判断存在环。这样子是可以解决判断环存在的问题了。
现在是,怎么把这些构成环的节点给找出来呢?或者说我们能否想到一个办法判断出哪些边构成了桥呢?在这里,我们可以在判断环存在的问题的基础上做一点修改。我们知道,判断一个环存在的话,它必然是在递归调用的过程中碰到了一个之前访问过的节点。如果我们用一个计数器来记录每次递归调用移动一步时的步数,那么这里就记录出来了从某个给定的起始节点到某个终点的步数。对于存在环的情况,在这个和之前访问过的节点相邻的节点,它到这个节点就可能有两个步数。而且两个节点一个大一个小。
这个时候,如果我们同时用另外一个数组来记录到这个节点的最短步数,那么必然这个在检测到构成环的节点位置它可以取到一个更小的步数值。如果我们循着这个思路往回退呢?就是说当我们递归调用回溯的时候,我们可以针对这个节点的前一个节点进行设置,也对应的取当前节点和它前一个节点的步数最小值。这样,我们可以这么一路倒退的回到环的起点。而这个最小步数就成了这个环里头所有元素的标记。从前面的讨论可以看到,只要存在环,那么它当前的最小步数就会比当前的累加步数小。这样就可以推导出环上的点了。
那么,对于非环上面的点呢?它不会存在有环路,所以走到某个点的时候,它必然就到头了,而这个时候,它的最小步数应该和累加步数是一样的。这样,我们就得到了一种寻找桥的方法了。
在实现的时候,我们可以采用两个数组,int[] pre, int[] low。其中pre数组用来保存记录当前的积累步数,而low节点在回溯的时候用来取它和相邻节点中的最小值。同时碰到环的时候也需要当前节点最小步数和目标节点累积步数中最小的那个。
按照这个思路,我们可以得到如下的代码:
public class Bridge { private int bridges; private int count; private int[] pre; private int[] low; public Bridge(Graph g) { low = new int[g.vertices()]; pre = new int[g.vertices()]; for(int i = 0; i < g.vertices(); i++) { low[i] = -1; pre[i] = -1; } for(int v = 0; v < g.vertices(); v++) if(pre[v] == -1) dfs(g, v, v); } public int components() { return bridges + 1; } private void dfs(Graph g, int u, int v) { pre[v] = count++; low[v] = pre[v]; for(int w : g.adj(v)) { if(pre[w] == -1) { dfs(g, v, w); low[v] = Math.min(low[v], low[w]); if(low[w] == pre[w]) { bridges++; } } else if(w != u) { low[v] = Math.min(low[v], pre[w]); } } } }
上述代码的过程有点不太好懂,我们可以以图中的示例为基础来演示一下变化。假设我们从节点0开始遍历,那么刚开始的时候调用的过程是dfs(g, 0, 0):
这个时候我们设置的pre, low数组值如下图:
根据节点的连接,假设节点0的下一个邻接节点是1,那么我们将递归的调用dfs(g, 0, 1):
假设节点1的下一个邻接节点是2,那么有下图:
假设2的下一个邻接节点是3,那么有:
这时候,如果3的下一个邻接节点是0,则相当于碰到了一个之前访问过的元素,也就是构成了环了。那么我们将有如下的变化:
因为之前已经访问过节点0了,所以这里要将节点3的最小步数也就是low[3] 设置为pre[0],也就是0。在这里,节点3的前一个节点是2,而它连接访问的下一个节点是0,所以可以进行这么一个判断。在这一步之后,我们的递归调用就应该要返回了。于是进入一个回溯的阶段。首先回溯的就是方法dfs(g, 2, 3):
在回溯的时候,我们会比较low[2], low[3],同时设置修改low[2] = Math.min(low[2], low[3]);这样,就使得low[2] = 0。这样,我们继续回溯到dfs(g, 1, 2),我们有如下的结果:
而这时候,我们有节点4还没有访问,于是又有方法dfs(g, 1, 4) :
对于节点4来说,它没有其他的相邻节点除了它的前一个节点1。所以当dfs(g, 1, 4)返回的时候,pre[4] == low[4],那么,1和4节点之间就构成了一个桥。
在这里我们也看到,当一个节点它没有访问到之前节点的时候,它的low节点值是不可能变化的,因此它的low节点和pre节点是一样的。这样也就找到了桥。
总结
关于图中间桥的查找和判断是一个有点难度的问题。它要借鉴图中间环的判断过程,同时也需要通过一种方式将环中间的节点依次给标注出来。这里利用深度优先遍历的递归和回溯过程,通过在回溯的时候将当前节点和它的相邻节点进行调整。这样如果节点步数没有变化则找到了桥边。这种利用的方式比较巧妙,值得反复推敲。
参考材料
http://algs4.cs.princeton.edu/41graph/Bridge.java.html
相关推荐
在标签中提到了“linux”,这意味着讨论的环境是在Linux操作系统下进行的,这通常是ROS的首选平台。`cv_bridge`是ROS的一部分,因此在Ubuntu这样的Linux发行版中,可以通过ROS的包管理系统(如`apt-get`)轻松安装和...
在本文中,我们将讨论 OpenStack 使用 Linux Bridge+VXLAN 模式的网络变化与分析。VXLAN(Virtual Extensible LAN)是一种-overlay 网络技术,能够在现有的网络基础设施上提供虚拟的 Layer 2 网络。 网络变化 在 ...
标题中的"ANSYS仿真案例Workbench有限元计算实例结果源文件流体fluent模型_double-bridge-overhead"表明,这是一个基于ANSYS Workbench平台的流体力学仿真案例,重点是利用FLUENT模块进行分析。在工程领域,ANSYS ...
本文主要讨论的是Intel的第二代智能酷睿处理器Sandy Bridge的深度测试。Sandy Bridge处理器是Intel在2011年推出的重要产品,它遵循了Intel的Tick-Tock发展战略。Tick-Tock模式指的是Intel处理器每两年进行一次工艺...
MATLAB是一款强大的数学计算软件,其图像处理工具箱是进行图像分析、转换和处理的常用平台。 描述中提到,“图像处理的各种基础变化,包括频域,时域的,适合基础自学。包括测试的图像。”这表明教程涵盖了图像处理...
最后,文档对全文内容进行了总结,并针对开关电源环路设计与计算中的几个关键问题展开了讨论。通过这份文档的学习,读者能够更加全面地理解开关电源的工作原理及其环路设计的重要性,为今后从事相关领域的研究或产品...
在这个场景下,可能是在讨论如何创建或配置一个私有网络,并使用桥接工具来使不同的计算资源(如虚拟机或容器)能够相互通信。这些计算资源可能分布在不同的网络段上,而桥接器则起到了连接它们的作用。 源码标签...
文档最后部分涉及了有关开关电源环路设计与计算的讨论。设计者需考虑到实际应用中的各种因素,如不同负载下的性能表现、温度变化对电源性能的影响以及电磁兼容性(EMC)等问题,确保设计的开关电源能在各种条件下...
接下来,我们要讨论的是"NuBridge_VCOM_DualPort0212.bin",这通常是牛桥设备的固件更新文件。固件是存储在硬件设备内部的软件,它控制设备的具体操作。固件更新有助于修复已知问题,增强设备性能,或者添加新功能。...
4. **微架构与Intel产品**:文档提及了Intel的微架构,比如Nehalem、Haswell、Sandy Bridge等,这些都是Intel处理器架构的代号。 5. **智能计算与服务器集群**:智能计算是指打破CPU边界,利用服务器集群进行复杂...
7. **论坛或讨论区**:参赛者交流经验、分享解题心得的地方,也是获取最新比赛动态的渠道。 了解蓝桥杯并参与其中,对于提升编程技能和算法理解大有裨益。参赛者可以通过这些资源来锻炼自己的编程思维,学习高级...
6. **系统级考虑**:讨论桥接对系统设计的影响,如时序约束、中断处理和错误管理。 7. **测试与验证**:提供验证桥接设计的方法,可能包括软件驱动程序开发和硬件测试平台的构建。 8. **应用实例**:展示桥接技术...
【标题】"nyubridge-24weeks"指的是纽约大学Tandon Bridge项目中的一个为期24周的学习计划,其中包含了编程课程的练习。这个项目旨在帮助学员提升在计算机科学领域的技能,特别是对于C++编程语言的理解和应用。通过...
标题中的"current.rar_bridge software_matlab 移相_全桥_全桥仿真_移相 全桥"揭示了本次讨论的核心内容,这是一个关于电力电子领域的技术主题,具体来说是使用MATLAB软件进行全桥移相器的仿真研究。全桥移相器在...
- **隔离型**:Forward、Flyback、Half Bridge、Full Bridge、Push Pull等,适合于需要电气隔离的应用场景。 - **整流型**:常见的有全桥整流、全波整流、同步整流等技术。 **1.2 调制方式** 开关电源的调制方式...
例如,使用介词+关系代词的结构来引导定语从句,例如:“A poem line describes a fight between a Turkish and a Bulgarian officer on a bridge off which they both fall into the river.” 此外,讲义还涉及同...
此节仅讨论ebtables的工作机制,而不涉及iptables。ebtables工作在链路层,即OSI模型中的第二层(或TCP/IP模型中的第一层)。当一个帧被桥接设备接收时,并不意味着这个帧的目的地就是本地计算机,因为桥接设备是在...
【标签】"sandyb" 这个标签通常用于标识与Sandy Bridge相关的内容,可能是讨论、教程或者技术文章,目的是为了方便用户快速找到与这个架构相关的资料。 【压缩包子文件的文件名称列表】:虽然提供的列表只有一个...