3

I'm having trouble finding a way to build a tree path from a list of related tuples? I only want a list of every path where each node is visited once, aka hamiltonian path.

I keep getting close but missing some path.

For example, let's say we have this list of connections:

connections = [(1, 4), (1, 5), (2, 5), (3, 4), (4, 1), (4, 3), (4, 5), (5, 1), (5, 2), (5, 4)]

desired output:

[[1,4,3], [1,4,5,2], [1,5,2], [1,5,4,3], 
 [2,5,1,4,3], [2,5,4,1], [2,5,4,3],
 [3,4,1,5,2], [3,4,5,1], [3,4,5,2], 
 [4, 3], [4,1,5,2], [4,5,1], [4,5,2],
 [5, 2], [5,1,4,3], [5,4,1], [5,4,3]
]

So each possible path is stored and each node is visited only once:

Here's what I have but it's missing a lot of paths:

def find_paths(current_vertex):
    if current_vertex not in current_path:
        current_path.append(current_vertex)

    possible_next_verticies = [v2 for v1,v2 in connections if v1 == current_vertex]

    # if the current vertex is in the current_path
    if current_vertex in current_path:
        # if all the possible_next_vertices are in the current_path, return
        adjacencies = [v for v in possible_next_verticies if v not in current_path]
        if not adjacencies:
            print "current_path: %s" % current_path
            if current_path not in TESTED_PATHS:
                TESTED_PATHS.append(current_path)
            current_path.remove(current_vertex)
            return

    for next_vertice in possible_next_verticies:
        if next_vertice not in current_path:
            current_path.append(next_vertice)
            find_paths(next_vertice)
            continue
        else:
            continue

    return current_path
Aaron McDaid
  • 24,484
  • 9
  • 56
  • 82
yekta
  • 3,133
  • 2
  • 32
  • 48
  • Are the edges directed? i.e. is the path allowed to go in either direction for each edge you specified? Is it sufficient to visit every node exactly once, or do you actually require a *cycle* where it returns to the first node at the end? Please edit the question accordingly – Aaron McDaid Feb 14 '15 at 17:58
  • I don't need it to cycle back to the first node, but just to visit every node possible (from each node), by visiting each node only once. – yekta Feb 14 '15 at 18:05
  • @yekta: then it is a *hamiltonian path*, not a *hamiltonian cycle*? – Willem Van Onsem Feb 14 '15 at 18:07
  • Yeah, it's not a cycle, I just need the path. – yekta Feb 14 '15 at 18:09

1 Answers1

3

OK, I was having so much trouble because of the data structure I was trying to work from, since there were duplicates in the original connections graph.

Better is to use a data structure like this:

connections = {1: [4, 5], 2: [5], 3: [4], 4: [1, 3, 5], 5: [1, 2, 4]} 

Then the following two algorithms can be used from https://www.python.org/doc/essays/graphs/

def find_path(graph, start, end, path=[]):
    path = path + [start]
    if start == end:
        return path
    if not graph.has_key(start):
        return None
    for node in graph[start]:
        if node not in path:
            newpath = find_path(graph, node, end, path)
            if newpath: return newpath
    return None

and for the full paths

def find_all_paths(graph, start, end, path=[]):
    path = path + [start]
    if start == end:
        return [path]
    if not graph.has_key(start):
        return []
    paths = []
    for node in graph[start]:
        if node not in path:
            newpaths = find_all_paths(graph, node, end, path)
            for newpath in newpaths:
                paths.append(newpath)
    return paths
yekta
  • 3,133
  • 2
  • 32
  • 48
  • Have you improved your solution over time? :) – Nikolay Fominyh Feb 13 '16 at 21:27
  • This doesn't seem to solve your initial question. It finds a path from a specific start node to a specific end node, but you wanted a Hamiltonian path that could start and end on any node (judging by your "desired output"). Your answer can easily loop through all starting nodes, but getting all Hamiltonian paths to arbitrary end nodes seems trickier with this algorithm. – tobiasvl Oct 02 '18 at 18:54
  • You can loop through all of the vertices and execute this function each time. But it is very inefficient, it will take forever when dealing with DAG with < 1000 vertices. – nosense Dec 13 '19 at 20:13