The hash map is populated as such:
{"1"=>["2"], "2"=>["3", "7"], "3"=>["4"], "5"=>["6", "2"], "6"=>["7"], "7"=>["8", "4"]}
so that each key can have multiple values. These values represent bus stops from a set of routes eg from bus stop 1 you can get to 2, from bus stop 2 you can get to 3 or 7 and so on.
I am trying to traverse this graph structure and find all possible paths of length greater than 1 from a given starting point. For example, for starting point of 1, the list of all possible paths would be
[[1,2] , [1,2,3] , [1,2,3,4] , [1,2,7] , [1,2,7,8], [1,2,7,4]]
I am trying to solve this problem recursively, where I iterate over the children of the current key (the values of that key) and call a function which essentially expands the children of that key. Here is my code so far:
if hash.has_key?(origin.to_s)
tmp = origin.to_s
tmp << hash[origin.to_s][0]
paths.push(tmp)
expand_node(hash,paths,paths.length-1,tmp[tmp.length-1])
end
Origin is the starting point. To start, I add the first path (in this case [1,2]
) to an array called paths
, then I expand the last value added to the previous path (in this case 2
).
def expand_node(hash,paths,curr_paths_index,node)
curr_node = node
if hash.has_key?(node.to_s)
counter = 0
hash[curr_node].each do |child|
tmp = paths[curr_paths_index]
tmp << child
paths << tmp
curr_paths_index += counter + 1
puts paths
expand_node(hash,paths,curr_paths_index,child)
end
end
end
The argument curr_paths_index
keeps track of the path from which I expand. The argument path
is the whole list of paths currently found. The argument node
is the current value being expanded.
Printing paths
after the function finishes yields:
123 123 1234 1234 1234 12347 12347 12347 12347 123478 123478 123478 123478 123478 1234784 1234784 1234784 1234784 1234784 1234784
Is there any way to modify this code so that it produces the desired output (shown above)? Is there a better way of solving this problem in general?
Thanks.
One way to solve a recursive problem is to first break it down into a single case that is easy to solve and then generalize that one case. Here is an easier case:
Given a graph and path through that graph, determine which nodes can be added to the end of that path without creating a loop.
If we can solve this problem we can easily solve the larger problem as well.
Note that if no new nodes are found in step 2, the recursive call will terminate because steps 3 and 4 have nothing to do.
Here is how I would solve step 2, I'll leave the recursive part to you
def find_next_nodes graph, path
graph[path[-1]].reject { |node| path.include? node }
end
I would do it like this, pretty sure this can be optimized further and there maybe an issue with performance too.
require 'set'
dt = {"1"=>["2"], "2"=>["3", "7"], "3"=>["4"], "5"=>["6", "2"], "6"=>["7"], "7"=>["8", "4"]}
def traverse(initial_hash, key_arrays, traversed_keys = [])
value = []
key_arrays.map do |key|
n = [*traversed_keys, key]
value << n unless n.count == 1 # prevent first key to be added
another_keys = initial_hash.fetch(key, nil) # try to load the value, fallback to nil if key not found
if another_keys.nil? # stop condition
value << n
elsif another_keys.is_a?(Enumerable) # if key found
another_keys.map do |ank| # recursive
value.concat([*traverse(initial_hash, [ank], n)]) # splat operator to unpack the array
end
end
end
value.to_set # only return unique value, can be converted to array if needed
end
traverse(dt, [1].map(&:to_s)).map { |val| val.join('') }
Sorry, but I have not debugged your code, so I think there's an issue with the temp variable there since the path which can't be expanded anymore is carried over to next iteration.
def doit(graph, start)
return [] unless graph.key?(start)
recurse(graph, start).drop(1)
end
def recurse(graph, start)
(graph[start] || []).each_with_object([[start]]) { |next_node, arr|
recurse(graph, next_node).each { |a| arr << [start, *a] } }
end
graph = { 1=>[2], 2=>[3, 7], 3=>[4], 5=>[6, 2], 6=>[7], 7=>[8, 4] }
doit(graph, 1)
#=> [[1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 7], [1, 2, 7, 8],
# [1, 2, 7, 4]]
doit(graph, 2)
#=> [[2, 3], [2, 3, 4], [2, 7], [2, 7, 8], [2, 7, 4]]
doit(graph, 3)
#=> [[3, 4]]
doit(graph, 5)
#=> [[5, 6], [5, 6, 7], [5, 6, 7, 8], [5, 6, 7, 4], [5, 2],
# [5, 2, 3], [5, 2, 3, 4], [5, 2, 7], [5, 2, 7, 8], [5, 2, 7, 4]]
doit(graph, 6)
#=> [[6, 7], [6, 7, 8], [6, 7, 4]]
doit(graph, 7)
#=> [[7, 8], [7, 4]]
doit(graph, 4)
#=> []
doit(graph, 99)
#=> []
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.