Reputation: 2234
I am trying to compute all simple paths between two nodes. I tried DFS and it seems DFS will not work.
The following figure make my goal more clear.
two nodes 'a' and 'f' is given, and have to find paths between them. for above example, there are two simple paths:
a ->b ->c ->e ->f
a ->b ->d ->e ->f
I checked some posts, but they seems don't handle cycles( without cycles my DFS works).
The graph is undirected and has cycles. I believe there is a solution for this. could anyone point me a reliable algorithm ? it would be great if such algorithm comes with pseudocode.
Thanks in advance Best,
Upvotes: 1
Views: 1353
Reputation: 36
Here is a Ruby solution using a weighted adj matrix and avoiding cycles. It is a little messy but it seams to work. I'm testing with a graph with nodes a,b,c,d,e,f in a connection loop (like a ring) but with two extra connections (b <-> f and c <-> f) generating cycles to test the algorithm.
# 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
Upvotes: 2