如何确定两个节点是否连接?

时间:2008-12-09 21:41:18

标签: graph-theory

我担心这可能会影响NP-Complete问题。我希望有人可以给我一个答案,不管它是否存在。而且我正在寻找更多的答案,而不仅仅是是或否。我想知道为什么。如果你可以说,“这基本上是这个问题'x',它不是NP-Complete。(维基百科链接)”

(不,这不是作业)

有没有办法确定两个点是否连接在任意非有向图上。例如,以下

Well
  |
  |
  A
  |
  +--B--+--C--+--D--+
  |     |     |     |
  |     |     |     |
  E     F     G     H
  |     |     |     |
  |     |     |     |
  +--J--+--K--+--L--+
                    |
                    |
                    M
                    |
                    |
                  House

点A到M(没有'I')是可以打开或关闭的控制点(如天然气管道中的阀门)。 '+'是节点(比如管道T),我猜Well和House也是节点。

我想知道我是否关闭了一个任意控制点(例如C)井和房子是否仍然连接(其他控制点也可能关闭)。例如,如果B,K和D关闭,我们仍然有通过A-E-J-F-C-G-L-M的路径,关闭C将断开井和房屋。当然;如果只是D被关闭,只关闭C不会断开众议院。

另一种说法是C桥/切边/地峡?

我可以将每个控制点视为图形上的权重(0表示打开,1表示关闭);然后找到Well和House之间的最短路径(结果> = 1表示它们已断开连接。我可以通过各种方法将算法短路以找到最短路径(例如,一旦路径达到1就丢弃路径,一旦我们有任何连接井和房子的路径等,就停止搜索。)当然,我也可以在放弃之前对要检查的跳数进行一些人为的限制。

之前有人必须将这类问题归类,我只是错过了这个名字。

11 个答案:

答案 0 :(得分:32)

您的描述似乎表明您只关心两个节点是否已连接,而不是找到最短路径。

查找两个节点是否相互连接相对简单:

Create two sets of nodes:  toDoSet and doneSet
Add the source node to the toDoSet 
while (toDoSet is not empty) {
  Remove the first element from toDoSet
  Add it to doneSet
  foreach (node reachable from the removed node) {
    if (the node equals the destination node) {
       return success
    }
    if (the node is not in doneSet) {
       add it to toDoSet 
    }
  }
}

return failure.

如果你对toDoSet和doneSet使用哈希表或类似的东西,我相信这是一个线性算法。

请注意,此算法基本上是标记和清除垃圾收集的标记部分。

答案 1 :(得分:6)

请参阅http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm,了解所有图表相关问题的一站式服务。我相信你的问题实际上可以在二次时间内解决。

答案 2 :(得分:5)

你不需要Dijkstra算法来解决这个问题,因为它使用了一个不需要的堆,并为你的复杂性引入了一个log(N)因子。这只是广度优先搜索 - 不包括封闭边缘作为边缘。

答案 3 :(得分:3)

找到最短路径的问题不是NP完全的。它被称为最短路径问题(最初足够),并且algorithms用于解决它的许多不同变体。

确定两个节点是否连接的问题也不是NP完全的。您可以使用从任一节点开始的深度优先搜索来确定它是否已连接到另一个节点。

答案 4 :(得分:2)

不是NP-complete,用一个众所周知的解决方案解决 - Dijkstra's Algorithm

答案 5 :(得分:2)

对我来说,似乎你正在寻求解决方案,但我有可能误解了这个问题。如果您喜欢说,并将闭合边1作为权重,则可以应用Dijkstra算法http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm。这应该可以解决O(E * lg(V))

中的问题

答案 6 :(得分:2)

假设你有一个邻接矩阵:

bool[,] adj = new bool[n, n];

如果在i和j之间存在开放路径并且bool [i,i] = false,则bool [i,j] = true。

public bool pathExists(int[,] adj, int start, int end)
{
  List<int> visited = new List<int>();
  List<int> inprocess = new List<int>();
  inprocess.Add(start);

  while(inprocess.Count > 0)
  {
    int cur = inprocess[0];
    inprocess.RemoveAt(0);
    if(cur == end)
      return true;
    if(visited.Contains(cur))
      continue;
    visited.Add(cur);
    for(int i = 0; i < adj.Length; i++)
      if(adj[cur, i] && !visited.Contains(i) && !inprocess.Contains(i))
        inprocess.Add(i);
  }
  return false;
}

这是上面算法的递归版本(用Ruby编写):

def connected? from, to, edges
  return true if from == to
  return true if edges.include?([from, to])
  return true if edges.include?([to, from])

  adjacent = edges.find_all { |e| e.include? from }
                  .flatten
                  .reject { |e| e == from }

  return adjacent.map do |a|
    connected? a, to, edges.reject { |e| e.include? from }
  end.any?
end

答案 7 :(得分:1)

如果您只需确定是否连接了2个节点,则可以使用集合,这比图算法更快。

  1. 将整个图表拆分为边缘。将每个边添加到一个集合中。
  2. 在下一次迭代中,在步骤2中创建的边缘的2个外部节点之间绘制边缘。这意味着将新节点(及其对应的集合)添加到原始边缘所在的集合中。 (基本上设置合并)
  3. 重复2,直到您要查找的2个节点位于同一组中。您还需要在步骤1之后进行检查(以防两个节点相邻)。
  4. 首先,您的节点将分别位于其集合中,

    o   o1   o   o   o   o   o   o2
     \ /     \ /     \ /     \ /
     o o     o o     o o     o o
       \     /         \     /
       o o o o         o o o o 
          \               /
           o o1 o o o o o o2
    

    随着算法的进展和合并,它的输入相对减半。

    在上面的例子中,我想看看o1和o2之间是否有路径。我在合并所有边之后才发现这条路径。某些图形可能具有单独的组件(断开连接),这使得您无法在最后设置一个组件。在这种情况下,您可以使用此算法测试连通性,甚至可以计算图表中组件的数量。组件数是算法完成时能够获得的集合数。

    可能的图表(对于上面的树):

    o-o1-o-o-o2
      |    |
      o    o
           |
           o
    

答案 8 :(得分:0)

Dijkstra是有点矫枉过正!! 只需使用A中的广度优先搜索来搜索您想要访问的节点。如果你找不到它,它就没有连接。每次搜索的复杂度为O(nm),小于Dijkstra。

有点相关的是最大流量/最小切割问题。查一查,它可能与您的问题有关。

答案 9 :(得分:0)

我看到您得到的答案是,它绝对不是NP-Complete,这也是一个非常老的问题。

但是,我将仅提出另一种方法来解决该问题。您可以为此使用不交集。在大多数情况下,对于给定的方案,该方法比进行图形遍历可以节省更多时间(对于大量测试而言,该时间包括恒定时间)。但是,如果使用按等级合并或路径压缩,则构建图形可能会花费大量时间。

您可以阅读有关数据结构here的信息。

答案 10 :(得分:-1)

如果你需要的只是找到一个节点是否连接到另一个节点,那么任何图形最短路径算法都会过度。一个很好的Java库可以实现JGraphT。它的用法很简单,这是一个Integer图的例子:

public void loadGraph() {
    // first we create a new undirected graph of Integers
    UndirectedGraph<Integer, DefaultEdge> graph = new SimpleGraph<>(DefaultEdge.class);

    // then we add some nodes
    graph.addVertex(1);
    graph.addVertex(2);
    graph.addVertex(3);
    graph.addVertex(4);
    graph.addVertex(5);
    graph.addVertex(6);
    graph.addVertex(7);
    graph.addVertex(8);
    graph.addVertex(9);
    graph.addVertex(10);
    graph.addVertex(11);
    graph.addVertex(12);
    graph.addVertex(13);
    graph.addVertex(14);
    graph.addVertex(15);
    graph.addVertex(16);

    // then we connect the nodes
    graph.addEdge(1, 2);
    graph.addEdge(2, 3);
    graph.addEdge(3, 4);
    graph.addEdge(3, 5);
    graph.addEdge(5, 6);
    graph.addEdge(6, 7);
    graph.addEdge(7, 8);
    graph.addEdge(8, 9);
    graph.addEdge(9, 10);
    graph.addEdge(10, 11);
    graph.addEdge(11, 12);
    graph.addEdge(13, 14);
    graph.addEdge(14, 15);
    graph.addEdge(15, 16);

    // finally we use ConnectivityInspector to check nodes connectivity
    ConnectivityInspector<Integer, DefaultEdge> inspector = new ConnectivityInspector<>(graph);

    debug(inspector, 1, 2);
    debug(inspector, 1, 4);
    debug(inspector, 1, 3);
    debug(inspector, 1, 12);
    debug(inspector, 16, 5);
}

private void debug(ConnectivityInspector<Integer, DefaultEdge> inspector, Integer n1, Integer n2) {
    System.out.println(String.format("are [%s] and [%s] connected? [%s]", n1, n2, inspector.pathExists(n1, n2)));
}

此lib还提供了所有最短路径算法。