【问题标题】:finding all simple paths between two points of a graph with cycles找到带环的图的两点之间的所有简单路径
【发布时间】:2016-01-22 10:27:04
【问题描述】:

我正在尝试计算两个节点之间的所有简单路径。 我尝试了 DFS,似乎 DFS 不起作用。

下图让我的目标更加明确。

给出了两个节点'a'和'f',并且必须找到它们之间的路径。 对于上面的例子,有两个简单的路径:

a ->b ->c ->e ->f

a ->b ->d ->e ->f

我检查了一些帖子,但它们似乎不处理周期(没有周期我的 DFS 工作)。

图是无向的并且有循环。我相信有一个解决方案。 谁能给我一个可靠的算法? 如果这样的算法带有伪代码就好了。

提前致谢 最好的,

【问题讨论】:

  • 实际上,您可以只执行递归 DFS,但始终将访问的节点作为参数提供,并确保您不会深入挖掘在当前 DFS 路径中已经访问过的节点(因此,避免循环)。请注意这与全局访问列表有何不同,因为它不允许您多次访问同一个节点。
  • 哦,另外,您进行的访问列表正是您找到最终节点时所需的路径。
  • 我应该提到的坏处是我尝试了非递归 DFS。但问题是当在上图中找到一个简单的路径 {a ->b ->c ->e ->f} 时,您会寻找第二条路径但 'e' 已被访问,因此无法进一步。递归 DFS 能解决这个问题吗?
  • 亲爱的 Andras Gyomrey,您的意思是在递归 DFS 中可以避免在非递归 DFS 中无法第二次访问同一节点的问题?有没有办法用非递归 DFS 做到这一点?
  • 如果您不使用全局 visited 列表,而是将其作为参数(而不是通过引用)传递给您的递归调用。这意味着对visited 列表的修改对于当前函数调用是本地的。一旦递归调用结束,所有标记为已访问的调用都将被遗忘,这使您可以通过另一个递归调用再次访问同一节点。

标签: graph path


【解决方案1】:

这是一个使用加权 adj 矩阵并避免循环的 Ruby 解决方案。它有点乱,但它似乎可以工作。 我正在测试一个带有节点 a、b、c、d、e、f 在连接循环(如环)中的图,但有两个额外的连接(b f 和 c f)生成循环测试算法。

# Generate all paths from A to E
@grapascii =
'
   C-----D
  / \    |
 B---F---E
  \ /     
   A
'

@nodes = %w{A B C D E F}
n = -1 # represents no connection

#weighted adj matrix
@adjmat = [
    #a  b  c  d  e  f
    [n, 1, n, n, n, 1], # a
    [1, n, 3, n, n, 1], # b
    [n, 4, n, 2, n, 1], # c
    [n, n, 4, n, 1, n], # d
    [n, n, n, 1, n, 1], # e
    [1, 6, 1, n, 2, n]  # f
]

# generate a neighbors hash for easy and fast future acccess
def gen_neighbors(nodes, adjmat)
    len = nodes.length
    neighbors = {}
    for i in 0..(len - 1)
        for j in 0..(len - 1)
            if adjmat[i][j] >= 0 && i != j
                neighbors[nodes[i]] ||= []
                neighbors[nodes[i]] << {:node => nodes[j], :cost => adjmat[i][j]}
            end
        end
    end
    return neighbors
end

@neighbors = gen_neighbors(@nodes, @adjmat)

def all_paths(currpath, destnode, currcost, paths)
    #the current node is always tha last one on the current evaluated path
    currnode = currpath.last
    # just the neighbors that is nor on the current evaluated path, to avoid cycles
    current_neighbors = @neighbors[currnode].select{|n| !currpath.include?(n[:node])}

    #each neighbor is a hash with :node and :cost
    current_neighbors.each do |neighbor|
        # yes. we have to duplicate that. maybe there is a better solution...
        new_path = currpath + [neighbor[:node]]
        cost = currcost + neighbor[:cost]

        if neighbor[:node] == destnode
            #FOUND PATH
            paths << {:path => new_path, :cost => cost}
        else
            all_paths(new_path, destnode, cost, paths)
        end
    end
end

puts @grapascii

puts "NEIGHBORS HASH:"
@neighbors.each do |node, neighbors|
    neighbors_and_cost = neighbors.map{|n| "#{n[:node]}(#{n[:cost]})"}
    puts "#{node} => #{neighbors_and_cost.join(', ')}"
end


# start path with the start node
startpath = ['A']
# paths variable will hold all possible paths without cycles
paths = []
all_paths(startpath, 'E', 0, paths)

puts "PATHS:"
# sort paths by total cost(optional)
paths.sort!{|a, b| a[:cost] <=> b[:cost]}
paths.each do |path|
    puts "#{path[:path]} => #{path[:cost]}"
end

输出:

   C-----D
  / \    |
 B---F---E
  \ /     
   A
NEIGHBORS HASH:
A => B(1), F(1)
B => A(1), C(3), F(1)
C => B(4), D(2), F(1)
D => C(4), E(1)
E => D(1), F(1)
F => A(1), B(6), C(1), E(2)
PATHS:
["A", "F", "E"] => 3
["A", "B", "F", "E"] => 4
["A", "F", "C", "D", "E"] => 5
["A", "B", "F", "C", "D", "E"] => 6
["A", "B", "C", "D", "E"] => 7
["A", "B", "C", "F", "E"] => 7
["A", "F", "B", "C", "D", "E"] => 13

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-03-21
    • 2012-06-05
    • 1970-01-01
    • 1970-01-01
    • 2021-09-19
    • 1970-01-01
    • 1970-01-01
    • 2011-02-22
    相关资源
    最近更新 更多