|
| 1 | +#!/usr/bin/env python3 |
| 2 | +# Find Eulerian Tour |
| 3 | +# |
| 4 | +# Write a program that takes in a graph |
| 5 | +# represented as a list of tuples |
| 6 | +# and return a list of nodes that |
| 7 | +# you would follow on an Eulerian Tour |
| 8 | +# |
| 9 | +# For example, if the input graph was |
| 10 | +# [(1, 2), (2, 3), (3, 1)] |
| 11 | +# A possible Eulerian tour would be [1, 2, 3, 1] |
| 12 | + |
| 13 | +def get_a_tour(): |
| 14 | + '''This function returns a possible tour in the current graph and removes the edges included in that tour, from the graph.''' |
| 15 | + global graph |
| 16 | + |
| 17 | + nodes_degree = {} # Creating a {node: degree} dictionary for current graph. |
| 18 | + for edge in graph: |
| 19 | + a, b = edge[0], edge[1] |
| 20 | + nodes_degree[a] = nodes_degree.get(a, 0) + 1 |
| 21 | + nodes_degree[b] = nodes_degree.get(b, 0) + 1 |
| 22 | + |
| 23 | + tour =[] # Finding a tour in the current graph. |
| 24 | + loop = enumerate(nodes_degree) |
| 25 | + while True: |
| 26 | + try: |
| 27 | + l = loop.__next__() |
| 28 | + index = l[0] |
| 29 | + node = l[1] |
| 30 | + degree = nodes_degree[node] |
| 31 | + try: |
| 32 | + if (tour[-1], node) in graph or (node, tour[-1]) in graph: |
| 33 | + tour.append(node) |
| 34 | + try: |
| 35 | + graph.remove((tour[-2], tour[-1])) |
| 36 | + nodes_degree[tour[-1]] -= 1 # Updating degree of nodes in the graph, not required but for the sake of completeness. |
| 37 | + nodes_degree[tour[-2]] -= 1 # Can also be used to check the correctness of program. In the end all degrees must zero. |
| 38 | + except ValueError: |
| 39 | + graph.remove((tour[-1], tour[-2])) |
| 40 | + nodes_degree[tour[-1]] -= 1 |
| 41 | + nodes_degree[tour[-2]] -= 1 |
| 42 | + except IndexError: |
| 43 | + tour.append(node) |
| 44 | + except StopIteration: |
| 45 | + loop = enumerate(nodes_degree) |
| 46 | + |
| 47 | + if len(tour) > 2: |
| 48 | + if tour[0] == tour[-1]: |
| 49 | + return tour |
| 50 | + |
| 51 | +def get_eulerian_tour(): |
| 52 | + '''This function returns a Eulerian Tour for the input graph.''' |
| 53 | + global graph |
| 54 | + tour = get_a_tour() |
| 55 | + |
| 56 | + if graph: # If stuck at the beginning, finding additional tour in the graph. |
| 57 | + loop = enumerate(tour[: -1]) |
| 58 | + l = loop.__next__() |
| 59 | + i = l[0] |
| 60 | + node = l[1] |
| 61 | + try: |
| 62 | + while True: |
| 63 | + if node in list(zip(*graph))[0] or node in list(zip(*graph))[1]: |
| 64 | + t = get_a_tour() # Retreivng the additional tour |
| 65 | + j = t.index(node) |
| 66 | + tour = tour[ : i] + t[j:-1] + t[ :j+1] + tour[i+1: ] # Joining the two tours. |
| 67 | + if not graph: # Found Eulerian Tour |
| 68 | + return tour # Returning the Eulerian Tour |
| 69 | + loop = enumerate(tour[: -1]) # Still stuck? Looping back to search for another tour. |
| 70 | + l = loop.__next__() |
| 71 | + i = l[0] |
| 72 | + node = l[1] |
| 73 | + except StopIteration: # Oops! seems like the vertices in the current tour cannot connect to rest of the edges in the graph. |
| 74 | + print("Your graph doesn't seem to be connected") |
| 75 | + exit() |
| 76 | + else: # Found the Eulerian Tour in the very first call. Lucky Enough! |
| 77 | + return tour |
| 78 | + |
| 79 | +# Sample inputs |
| 80 | +# graph = [(1, 2), (1, 3), (2, 3), (2, 4), (2, 6), (3, 4), (3, 5), (4, 5), (4, 6)] |
| 81 | +# graph = [(1, 2), (1, 3), (2, 3)] |
| 82 | +# graph = [(1, 2), (1, 3), (2, 3), (2, 4), (2, 6), (3, 4), (3, 5), (4, 5), (4, 6), (9, 10), (10, 11), (11, 9)] |
| 83 | +# graph = [(1, 2), (1, 3), (2, 3), (2, 4), (2, 6), (3, 4), (3, 5), (4, 5), (4, 6), (2, 7), (7, 8), (8, 2)] |
| 84 | +# graph = [(1, 2), (1, 3), (2, 3), (2, 4), (2, 6), (3, 4), (3, 5), (4, 5), (4, 6), (1, 5), (5, 6), (1, 6)] |
| 85 | +# graph = [(1, 2), (2, 3), (3, 1), (3, 4), (4, 3)] |
| 86 | +# graph = [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)] |
| 87 | +# graph = [(2, 6), (4, 2), (5, 4), (6, 5), (6, 8), (7, 9), (8, 7), (9, 6)] |
| 88 | + |
| 89 | +# creating a {node: degree} dictionary |
| 90 | +nodes_degree = {} |
| 91 | +for edge in graph: |
| 92 | + a, b = edge[0], edge[1] |
| 93 | + nodes_degree[a] = nodes_degree.get(a, 0) + 1 |
| 94 | + nodes_degree[b] = nodes_degree.get(b, 0) + 1 |
| 95 | + |
| 96 | +#checking degree |
| 97 | +degrees = nodes_degree.values() # remember it return a view |
| 98 | +for degree in degrees: |
| 99 | + if degree % 2: |
| 100 | + print("Your graph have one or more nodes with odd degrees. Hence an Eulerian Tour is impossible.") |
| 101 | + exit() |
| 102 | + |
| 103 | +#finding Eulerian Tour |
| 104 | +tour = get_eulerian_tour() |
| 105 | +print(tour) |
0 commit comments