算法:节点A和节点B在图中是否相连

3
我正在寻找一种算法,用于检查在图形上任意两个节点之间的任何有效连接(最短或最长)。
我的图形被固定在一个网格上,具有逻辑(x,y)坐标和北/南/东/西连接,但节点可以随机删除,因此不能假设取距目标最近的坐标边缘总是能够到达目标。
代码使用 Python 编写。数据结构为每个节点(对象)都有一个连接节点列表。列表元素是对象引用,因此我们可以递归地搜索该节点的连接节点列表,如下所示:
for pnode in self.connected_nodes:
    for cnode in pnode.connected_nodes:
        ...etc

我已经包含了一张图表,展示了节点如何映射到x、y坐标以及它们是如何在北/东/南/西方向上连接的。有时会缺少节点(即J和K之间),有时会缺少边缘(即G和H之间)。节点和边缘的存在是不稳定的(虽然当我们运行算法时,它会在固定的时间点进行快照),只能通过检查每个节点的连接节点列表来确定。
算法需要产生一个简单的true/false,以确定两个节点之间是否存在有效连接。递归遍历每个连接节点列表会导致操作数量爆炸——如果节点相隔n个边缘,则最多需要4^n次操作。我理解的是,Dijistrka算法之类的算法是基于边权重寻找最短路径的,但如果根本没有连接,它仍然能工作吗?
对于一些背景,我正在使用这个模型来建模二维可摧毁物体。每个节点表示一块材料,如果一个或多个节点与其余材料没有连接,则应该分离。在图表中,D、H、R应该从主体中分离出来,因为它们没有连接。
更新:
虽然许多发布的答案都可能有效,但DFS快速、简单且非常合适。我不喜欢在具有高价值权重的节点之间添加额外的边缘以使用Dijkstra,因为节点本身可能会消失,而不仅仅是边缘。SSC方法似乎更适合区分强连接和弱连接的图形部分,在我的图表中,如果G和H之间有单个边缘,则可以工作。
这是我的DFS搜索实验代码,它创建了与图表中所示相同的图形。
class node(object):
    def __init__(self, id):
        self.connected_nodes  = []
        self.id               = id

    def dfs_is_connected(self, node):
        # Initialise our stack and our discovered list
        stack      = []
        discovered = []

        # Declare operations count to track how many iterations it took
        op_count = 0

        # Push this node to the stack, for our starting point
        stack.append(self)

        # Keeping iterating while the stack isn't empty
        while stack:
            # Pop top element off the stack
            current_node = stack.pop()

            # Is this the droid/node you are looking for?
            if current_node.id == node.id:
                # Stop!
                return True, op_count

            # Check if current node has not been discovered
            if current_node not in discovered:

                # Increment op count
                op_count += 1

                # Is this the droid/node you are looking for?
                if current_node.id == node.id:
                    # Stop!
                    return True, op_count

                # Put this node in the discovered list
                discovered.append(current_node)

                # Iterate through all connected nodes of the current node
                for connected_node in current_node.connected_nodes:
                    # Push this connected node into the stack
                    stack.append(connected_node)

        # Couldn't find the node, return false. Sorry bud
        return False, op_count


if __name__ == "__main__":

    # Initialise all nodes
    a = node('a')
    b = node('b')
    c = node('c')
    d = node('d')
    e = node('e')
    f = node('f')
    g = node('g')
    h = node('h')
    j = node('j')
    k = node('k')
    l = node('l')
    m = node('m')
    n = node('n')
    p = node('p')
    q = node('q')
    r = node('r')
    s = node('s')

    # Connect up nodes
    a.connected_nodes.extend([b, e])
    b.connected_nodes.extend([a, f, c])
    c.connected_nodes.extend([b, g])
    d.connected_nodes.extend([r])
    e.connected_nodes.extend([a, f, j])
    f.connected_nodes.extend([e, b, g])
    g.connected_nodes.extend([c, f, k])
    h.connected_nodes.extend([r])
    j.connected_nodes.extend([e, l])
    k.connected_nodes.extend([g, n])
    l.connected_nodes.extend([j, m, s])
    m.connected_nodes.extend([l, p, n])
    n.connected_nodes.extend([k, m, q])
    p.connected_nodes.extend([s, m, q])
    q.connected_nodes.extend([p, n])
    r.connected_nodes.extend([h, d])
    s.connected_nodes.extend([l, p])

    # Check if a is connected to q
    print a.dfs_is_connected(q)
    print a.dfs_is_connected(d)
    print p.dfs_is_connected(h)

1
你是否了解networkx库?类似这个的东西会有所帮助吗? - Ioannis
@loannis 看起来不错。通过查看他们的源代码,它似乎使用了迪杰斯特拉或类似的算法,并在找不到路径时抛出异常。我已经有一些能够工作的代码,只需要大约16行左右,所以我会坚持使用它,而不是导入一个全新的模块。虽然,如果我需要做更多的图形/树算法,可能值得切换到像networkx这样的东西。我有一个在每个机会都重复造轮子的习惯。 - Oliver
实际上,指针更多是为了提高对NetworkX的意识,这可能在其他场合也会有用。 - Ioannis
4个回答

1
要找出这个信息,您只需要在节点之一上运行简单的 DFSBFS 算法,它将找到图中连续组件中的所有可达节点,因此如果您在算法运行期间找到了另一个节点,您只需将其标记下来即可。

1
直觉上,深度优先搜索(DFS)听起来像是最慢的方法,但仔细思考后,它变得越来越有意义。一些谷歌搜索表明这是检查连通图的常见方法。我在问题中包含了我的测试代码以供参考。 - Oliver

1
有一种方法可以使用Dijkstra算法来查找路径。如果两个节点之间有边,则将权重设为1,如果没有节点,则将其权重设置为sys.maxint。然后,当计算最小路径时,如果它大于节点数,则它们之间没有路径。
另一种方法是首先找到图的强连接组件。如果节点在同一个强连接组件中,则使用Dijkstra算法查找路径,否则它们之间没有连接路径。

0
你可以看一下A*寻路算法(它使用启发式方法使其比Dijkstra更有效,因此如果您的问题中没有任何可以利用的东西,您最好使用Dijkstra算法。但是您需要正权重。如果您的图中没有这样的内容,您可以简单地给每个边缘赋予一个权重为1)。
从维基百科上的伪代码来看,A*通过获取当前节点的邻居来移动到另一个节点。Dijkstra算法保持邻接列表,以便知道哪些节点彼此相连。
因此,如果您从节点H开始,您只能去RD。由于这些节点与其他节点没有连接,因此该算法不会经过其他节点。

0
你可以找到图的强连通组件(SCC),然后检查感兴趣的节点是否在一个组件中。在你的例子中,H-R-D将是第一个组件,其余的是第二个组件,所以对于H和R结果为true,但对于H和A则为false。 查看这里的SCC算法: https://class.coursera.org/algo-004/lecture/53

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接