我遇到wait-for graphs,我想知道,是否有任何有效的算法来检测是否向有向图添加边缘会导致循环?
有问题的图表是可变的(他们可以添加或删除节点和边缘)。而且我们对实际知道一个有问题的周期并不感兴趣,只知道有一个是足够的(以防止添加一个有问题的边缘)。
当然可以使用算法来计算强连通组件(例如Tarjan)以检查新图是否是非循环的,但每次添加边时再次运行它似乎效率很低。 / p>
答案 0 :(得分:34)
如果我正确地理解了你的问题,那么只有在之前没有从v到u的路径时才会插入新的边缘(u,v)(即,如果(u,v)没有创建一个循环)。因此,您的图形始终是DAG(有向非循环图)。使用Tarjan算法检测强连通分量(http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm)在这种情况下听起来有点过分。在插入(u,v)之前,您需要检查的是是否存在从v到u的有向路径,这可以通过简单的BFS / DFS来完成。
所以最简单的方法是:(n = | V |,m = | E |):
尽管在最坏的情况下插入(u,v)需要花费O(m)时间,但在你的情况下可能会非常快。当从v开始执行BFS / DFS以检查是否可以访问时,您只访问可从v访问的顶点。我猜想在您的设置中,图形非常稀疏,并且另一个可到达的顶点数不是那个高。
但是,如果你想改善理论运行时间,这里有一些提示(大多数表明这不会很容易)。假设我们的目标是在O(1)时间内测试是否存在从v到u的有向路径。此上下文中的关键字是DAG的传递闭包(即,当且仅当在DAG中存在从u到v的有向路径时,包含边(u,v)的图) 。不幸的是,在动态环境中保持传递闭包似乎并非那么简单。有几篇论文考虑了这个问题,我发现的所有论文都是STOC或FOCS论文,这表明它们非常<非常。我发现的最新(也是最快)的结果是由Sankowski(http://dl.acm.org/citation.cfm?id=1033207)撰写的文章 Dynamic Transitive Closure via Dynamic Matrix Inverse 。
即使您愿意理解其中一种动态传递闭包算法(或者甚至想要实现它),它们也不会因为以下原因而加速。这些算法是针对这种情况而设计的,在这种情况下,您有很多连接查询(然后可以在O(1)时间内执行),并且图中只有少量更改。那么目标是使这些变化比重新计算传递闭包更便宜。但是,单次检查连接时,此更新仍然较慢。因此,如果您需要对每个连接查询进行更新,最好使用上面提到的简单方法。
那么为什么我提到这种保持传递闭包的方法,如果它不符合你的需要呢?好吧,它表明搜索只消耗O(1)查询时间的算法可能不会比使用BFS / DFS的简单算法更快地找到解决方案。您可以尝试的是获得比O(m)更快但比O(1)更快的查询时间,而更新也比O(m)快。这是一个非常有趣的问题,但在我看来这是一个非常雄心勃勃的目标(所以也许不要花太多时间去尝试实现它。)。
答案 1 :(得分:5)
正如Mark建议的那样,可以使用存储连接节点的数据结构。最好使用布尔矩阵|V|x|V|
。可以使用Floyd-Warshall算法初始化值。这是在O(|V|^3)
中完成的。
让T(i)
设置包含顶点i
路径的顶点,以及F(j)
顶点集合,其中存在来自顶点j
的路径。首先是i
第{第1行}中的第一行和第j
列中的第二行。
添加边(i,j)
是一项简单的操作。如果之前未连接i
和j
,则来自a
的每个T(i)
和来自b
的每个F(j)
设置矩阵元素(a,b)
为真。但操作并不便宜。在最坏的情况下,它是O(|V|^2)
。这是在有向线的情况下,并且从末端到起始顶点添加边缘使得所有顶点连接到所有其他顶点。
删除边(i,j)
并不是那么简单,但在最坏的情况下并不是更昂贵的操作:-)如果在删除边缘之后存在从i
到j
的路径,没有什么变化。用Dijkstra检查,小于O(|V|^2)
。不再连接的顶点是(a,b)
:
a
T(i)
- i
- T(j)
,
在b
+ F(j)
中j
删除边T(j)
时只更改(i,j)
,因此必须重新计算。这是通过任何类型的图遍历(BFS,DFS),通过从顶点j
进入相反的边缘方向来完成的。这比O(|V|^2)
更少。由于矩阵元素的设置在最坏的情况下再次为O(|V|^2)
,因此该操作具有与添加边缘相同的最坏情况复杂度。
答案 2 :(得分:0)
如果以前的所有作业都按拓扑排序顺序排列。然后,如果你添加一个似乎制动排序的边缘,并且无法修复,那么你就有了一个循环。
https://stackoverflow.com/a/261621/831850
因此,如果我们有一个排序的节点列表:
1, 2, 3, ..., x, ..., z, ...
Such that each node is waiting for nodes to its left.
假设我们想要从x-&gt; z添加边。好吧,这似乎制动了那种。因此,我们可以将x处的节点移动到位置z + 1,这将修复排序,如果没有任何节点(x,z)在x处有节点边缘。
答案 3 :(得分:0)
如果图形是定向的,您只需要检查新边缘应该开始的节点的父节点(向上导航直到到达根节点)。如果其中一个父节点等于边的末端,则添加边将创建一个循环。