|
| 1 | +''' |
| 2 | +------------------------------------- Fleury's Algorithm ------------------------------------- |
| 3 | +
|
| 4 | +Approach:- |
| 5 | +1. Start with any vertex in the graph. |
| 6 | +
|
| 7 | +2. While there are unused edges in the graph, do the following steps: |
| 8 | + a. Choose any unused edge connected to the current vertex. It doesn't matter which one you choose. |
| 9 | + b. If removing the chosen edge doesn't disconnect the graph, go to the vertex at the other end of the chosen edge. |
| 10 | + c. If removing the chosen edge disconnects the graph, backtrack to the previous vertex that still has unused edges and choose a different edge. |
| 11 | + d. Repeat steps (a) to (c) until you can no longer choose any unused edges from the current vertex. |
| 12 | +
|
| 13 | +3. The algorithm terminates when you have traversed all the edges of the graph. |
| 14 | +
|
| 15 | +4. If all the vertices in the graph have even degrees, you will end up with an Eulerian circuit, which is a closed path that visits each edge exactly once. |
| 16 | +
|
| 17 | +5. If exactly two vertices in the graph have odd degrees, you will end up with an Eulerian path, which is a path that starts and ends at different vertices and visits each edge exactly once. |
| 18 | +''' |
| 19 | + |
| 20 | +# Program Starts |
| 21 | +from collections import defaultdict |
| 22 | + |
| 23 | +class Graph: |
| 24 | + |
| 25 | + def __init__(self,vertices): |
| 26 | + self.V= vertices #No. of vertices |
| 27 | + self.graph = defaultdict(list) |
| 28 | + self.Time = 0 |
| 29 | + |
| 30 | + # function to add an edge to graph |
| 31 | + def addEdge(self,source,destination): |
| 32 | + self.graph[source].append(destination) |
| 33 | + self.graph[destination].append(source) |
| 34 | + |
| 35 | + # This function removes edge source-destination from graph |
| 36 | + def removeEdge(self, source, destination): |
| 37 | + for index, key in enumerate(self.graph[source]): |
| 38 | + if key == destination: |
| 39 | + self.graph[source].pop(index) |
| 40 | + for index, key in enumerate(self.graph[destination]): |
| 41 | + if key == source: |
| 42 | + self.graph[destination].pop(index) |
| 43 | + |
| 44 | + # A DFS based function to count reachable vertices from destination |
| 45 | + def DFSCount(self, destination, visited): |
| 46 | + count = 1 |
| 47 | + visited[destination] = True |
| 48 | + for i in self.graph[destination]: |
| 49 | + if visited[i] == False: |
| 50 | + count = count + self.DFSCount(i, visited) |
| 51 | + return count |
| 52 | + |
| 53 | + # The function to check if edge source-destination can be considered as next edge in Euler Trail |
| 54 | + def isValidNextEdge(self, source, destination): |
| 55 | + # The edge source-destination is valid in one of the following two cases: |
| 56 | + |
| 57 | + # 1) If destination is the only adjacent vertex of source |
| 58 | + if len(self.graph[source]) == 1: |
| 59 | + return True |
| 60 | + else: |
| 61 | + ''' |
| 62 | + 2) If there are multiple adjacents, then source-destination is not a bridge |
| 63 | + Do following steps to check if source-destination is a bridge |
| 64 | +
|
| 65 | + 2.a) count of vertices reachable from source''' |
| 66 | + visited =[False]*(self.V) |
| 67 | + count1 = self.DFSCount(source, visited) |
| 68 | + |
| 69 | + '''2.b) Remove edge (source, destination) and after removing the edge, count |
| 70 | + vertices reachable from source''' |
| 71 | + self.removeEdge(source, destination) |
| 72 | + visited =[False]*(self.V) |
| 73 | + count2 = self.DFSCount(source, visited) |
| 74 | + |
| 75 | + #2.c) Add the edge back to the graph |
| 76 | + self.addEdge(source,destination) |
| 77 | + |
| 78 | + # 2.d) If count1 is greater, then edge (source, destination) is a bridge |
| 79 | + return False if count1 > count2 else True |
| 80 | + |
| 81 | + |
| 82 | + # Print Euler Trail starting from vertex source |
| 83 | + def printEulerUtil(self, source): |
| 84 | + #Recur for all the vertices adjacent to this vertex |
| 85 | + for destination in self.graph[source]: |
| 86 | + #If edge source-destination is not removed and it's a a valid next edge |
| 87 | + if self.isValidNextEdge(source, destination): |
| 88 | + print("%d-%d " %(source,destination)), |
| 89 | + self.removeEdge(source, destination) |
| 90 | + self.printEulerUtil(destination) |
| 91 | + |
| 92 | + '''The main function that print Eulerian Trail. It first finds an odd |
| 93 | +degree vertex (if there is any) and then calls printEulerUtil() |
| 94 | +to print the path ''' |
| 95 | + def printEulerTrail(self): |
| 96 | + #Find a vertex with odd degree |
| 97 | + source = 0 |
| 98 | + for i in range(self.V): |
| 99 | + if len(self.graph[i]) %2 != 0 : |
| 100 | + source = i |
| 101 | + break |
| 102 | + # Print Trail starting from odd vertex |
| 103 | + print ("\n") |
| 104 | + self.printEulerUtil(source) |
| 105 | + |
| 106 | +# Driver program |
| 107 | +V = int(input("\nEnter the number of vertices in the graph: ")) |
| 108 | + |
| 109 | +g = Graph(V) |
| 110 | + |
| 111 | +E = int(input("\nEnter the number of edges in the graph: ")) |
| 112 | + |
| 113 | +# Taking input from the user |
| 114 | +print("\nEnter the edges in the format (source destination)") |
| 115 | +for i in range(E): |
| 116 | + source = int(input(f"Source {i+1}: ")) |
| 117 | + destination = int(input(f"Destination {i+1}: ")) |
| 118 | + g.addEdge(source, destination) |
| 119 | + |
| 120 | +# Printing the final result after analysing |
| 121 | +print("\nResult of Fleury Algorithm: ", end="") |
| 122 | +g.printEulerTrail() |
| 123 | +print() |
0 commit comments