def topological_path(self,graph,startVertex,endVertex): path = graph.topologicalSort() dist, parent = ht.HashTable(),ht.HashTable() dist[startVertex] = 0 parent[startVertex] = None startVertexFound = False for vertex in path: if(vertex == startVertex): startVertexFound = True if(not startVertexFound): continue if(dist[vertex] == None): continue for neig,weight in graph.__neighbourNode__(vertex): if(dist[neig] == None or dist[neig] > dist[vertex] + weight): dist[neig] = dist[vertex] + weight parent[neig] = vertex l=[] startVertexFound = False curr = endVertex wt = 0 while(curr !=None): l.append(curr) end = curr curr = parent[curr] if(curr!=None): wt += graph.getValueOf((curr,end)) if(curr == startVertex): startVertexFound = True l.append(startVertex) break l.reverse() if(startVertexFound): return l,wt else: return None
def shortestPathWeighted(self,graph,startVertex,endVertex): record, parent = ht.HashTable(), ht.HashTable() record[startVertex] = 0 parent[startVertex] = None recursionTrace,rescueRepetition = ht.HashTable(), ht.HashTable() self.__shortestPathWeighted__(graph,startVertex,parent,record,recursionTrace,rescueRepetition) l=[endVertex] while(endVertex!=None): endVertex=parent[endVertex] l.append(endVertex) l.pop() l.reverse() return l
def __init__(self, vertices = None, edges = None, isUndirectedGraph = True, dtype=str): self._hashTable_ = ht.HashTable() self._reverse_hashTable_ = ht.HashTable() self.noVertices = 0 self.noEdges = 0 self.dtype = dtype self._isNegativeEdges_ = False self._isWeighted_ = False self.isUndirectedGraph = isUndirectedGraph if(vertices!=None): self.initializeVertices(vertices) if(edges!=None): self.initializeEdge(edges)
def __shortestPathWeighted__(self,graph,startVertex,parent,record,recursionTrace,rescueRepetition): for neig in graph[startVertex]: if(neig not in recursionTrace): weight = graph._hashTable_[startVertex][neig] if(record[neig] == None): record[neig] = weight + record[startVertex] parent[neig] = startVertex elif(record[neig] > weight + record[startVertex]): record[neig] = weight + record[startVertex] parent[neig] = startVertex elif(record[neig] <= weight + record[startVertex]): # Here we are recording the that has weight > current weight if(startVertex not in rescueRepetition): # In recursive call we will make sure that these edges should not be taken into consideraton rescueRepetition[startVertex] = ht.HashTable() rescueRepetition[startVertex][neig] = None else: rescueRepetition[startVertex][neig] = None for neig in graph[startVertex]: if(neig not in recursionTrace):# and neig not in rescueRepetition): if(startVertex in rescueRepetition and neig in rescueRepetition[startVertex]): pass else: recursionTrace[neig] = startVertex self.__shortestPathWeighted__(graph,neig,parent,record,recursionTrace,rescueRepetition) del recursionTrace[neig] if(startVertex in rescueRepetition and neig in rescueRepetition[startVertex]): del rescueRepetition[startVertex][neig]
def isCycleExist( self): # Time complexity O(|V| + |E|), Space complexity O(|V|) parent = ht.HashTable( ) # parent is a HashTable of visited vertices Space Complexity: O(|V|) for vertex in self.vertices( ): # Time complexity O(|V|), Space complexity O(|V|) l = [ vertex ] # l is a list of vertices reachable from vertex Space Complexity: O(|V|) if vertex not in parent: # parent is a HashTable so Time complexity O(1) parent[vertex] = None for next_vert in self.__cycleDFSHelper__( vertex, parent, l): # Space Complexity: O(1), Time Complexity: O(|V|) for i in range( len(l) ): # in case of unDirected we dont check for last vertex in l if (self.isUndirectedGraph == True and i == len(l) - 2): continue startVert, endVert = next_vert, l[i] table = self._hashTable_[startVert] if (endVert in table): return True # table is a HashTable so Time complexity O(1) return False
def __DFS_List__(self,vertices): #vertex = self.dtype(vertex) parent = ht.HashTable() for vertex in vertices: if vertex not in parent: l=[vertex] parent[vertex] = None for v in self.__DFS_Vertex__(vertex,parent): l.append(v) yield l
def DFS(self,v): if(type(v) == list): yield from self.__DFS_List__(v) else: vertex = self.dtype(v) parent = ht.HashTable() parent[vertex] = None yield vertex yield from self.__DFS_Vertex__(vertex,parent)
def BFS(self,vertex): vertex = self.dtype(vertex) # Here we require to index by our vertices but its not sure that our vertices are represented by numbers # thats why we use hashTable otherwise there is a simpler implementation of same code using list,numpy array insted of hashtable :) # so we use hashTable insted level,parent = ht.HashTable(),ht.HashTable() i = 1 level[vertex], parent[vertex] = 0, None frontier = [vertex] while(frontier): next = [] for u in frontier: for v_key in self[u]: if v_key not in level: level[v_key] = i parent[v_key] = u next.append(v_key) frontier = next i += 1 return level, parent
def topologicalSort(self): if(self.isUndirectedGraph): return if(self.isCycleExist()): return parent = ht.HashTable() l = [] for vert in self.vertices(): if(vert not in parent): parent[vert] = None self.__topologicalSort__(vert,parent,l) l.append(vert) l.reverse() return l
def bellman_ford(self,graph,startVertex,endVertex): def relax(u,v,w,dist,parent): if(dist[u] == None): return if(dist[v] == None or dist[v] > dist[u] + w): dist[v] = dist[u] + w parent[v] = u # Initialization parent = ht.HashTable() dist = ht.HashTable() for vert in graph.vertices(): parent[vert],dist[vert] = None,None dist[startVertex] = 0 # Bellman-Ford Algo for _ in range(graph.noOfVertices()): for u,v,w in graph.edges(): relax(u,v,w,dist,parent) # Check if there is any -ve cycle in path of startVertex for u,v,w in graph.edges(): if(dist[u] == None):continue if(dist[v] > dist[u] + w): return None return self.__htable_to_list__(parent,dist,endVertex)
def dijkstraV1(self,graph,startVertex,endVertex): if(startVertex == endVertex): return [startVertex,endVertex], 0 if(graph._isNegativeEdges_): return def extract_min(dist,Q): # O(|V|) minimum, ret_vert = None, None for vert, dump in Q: weight = dist[vert] if(weight == None): continue if(minimum == None): minimum = weight ret_vert = vert else: if(weight < minimum): minimum = weight ret_vert = vert return minimum,ret_vert # Initialization V = graph.getVertices() Q = ht.HashTable() for vert in V: Q[vert] = None parent = ht.HashTable() for vert in V: parent[vert] = None dist = ht.HashTable() for vert in V: dist[vert] = None dist[startVertex] = 0 # Dijkstra Algorithm while(len(Q) != 0): minimum, vert = extract_min(dist,Q) if(vert == None): break del Q[vert] for vertex,weight in graph.__neighbourNode__(vert): if(dist[vertex] == None or dist[vertex] > dist[vert] + weight): dist[vertex] = dist[vert] + weight parent[vertex] = vert return self.__htable_to_list__(parent,dist,endVertex)
def dijkstraV2(self,graph,startVertex,endVertex): # ===================================================================== def extract_min(q): q[-1][3]=0 q[0],q[-1] = q[-1], q[0] ret = q.pop() min_heapify(q,len(q),0) return ret # ===================================================================== def min_heapify(a,n,i): # O(lg(V)) if(2*i + 1 < n): if(a[i][0] == None and a[2*i + 1][0] != None): a[i], a[2*i + 1] = a[2*i + 1], a[i] a[i][3], a[2*i + 1][3] = a[2*i + 1][3], a[i][3] min_heapify(a,n,2*i + 1) elif(a[i][0]!=None and a[2*i + 1][0] !=None): if(a[i][0] > a[2*i + 1][0]): a[i], a[2*i + 1] = a[2*i + 1], a[i] a[i][3], a[2*i + 1][3] = a[2*i + 1][3], a[i][3] min_heapify(a,n,2*i + 1) if(2*i + 2 < n): if(a[i][0] == None and a[2*i + 2][0] != None): a[i], a[2*i + 2] = a[2*i + 2], a[i] a[i][3], a[2*i + 2][3] = a[2*i + 2][3], a[i][3] min_heapify(a,n,2*i + 2) elif(a[i][0] != None and a[2*i + 2][0] != None): if(a[i][0] > a[2*i + 2][0]): a[i], a[2*i + 2] = a[2*i + 2], a[i] a[i][3], a[2*i + 2][3] = a[2*i + 2][3], a[i][3] min_heapify(a,n,2*i + 2) # ===================================================================== def move_up(Q,H,vertex): #O(lg(V)) vertex = vertex[1] curr_Q = H[vertex] index = curr_Q[3] while(index>0): parent_index = (index - 1)//2 if(Q[parent_index][0] == None or Q[parent_index][0] > Q[index][0]): Q[parent_index], Q[index] = Q[index], Q[parent_index] Q[parent_index][3], Q[index][3] = Q[index][3], Q[parent_index][3] else: break index = parent_index # ===================================================================== def relax(u,v,weight,Q,H): # O(lg(V)) curr_Q_value_v = H[v] # it will go to H and return list refrence from Q of vertex v curr_Q_value_u = H[u] if(curr_Q_value_v[0] == None or curr_Q_value_v[0] > curr_Q_value_u[0] + weight): curr_Q_value_v[0] = curr_Q_value_u[0] + weight curr_Q_value_v[2] = u move_up(Q,H,curr_Q_value_v) # ===================================================================== if(graph._isNegativeEdges_): return # Initialization Q = [[0,startVertex,None,0]] # Here Q will record the distance path b/w vertices 1st element: cost, 2nd element: vertex, 3rd element: parent, 4th element: index H = ht.HashTable() # we require this hashTable cause in relax part if we don't use hash table then # to updating the cost of vertex v in Q we have to scan whole array Q to find v and then update cost i = 0 # H provides us element of Q in O(1) i.e. it will give us Q[v] in O(1) H[startVertex] = Q[i] for vertex in graph.vertices(): if(vertex == startVertex): continue i += 1 Q.append([None,vertex,None,i]) H[vertex] = Q[i] S = [] # Dijkstra Algorithm while(len(Q) != 0): u = extract_min(Q) S.append(u) if(u[0] == None): break for vertex,weight in graph.__neighbourNode__(u[1]): relax(u[1],vertex,weight,Q,H) # Finally return the path parent = ht.HashTable() weig = ht.HashTable() for i in range(len(S)): parent[S[i][1]] = S[i][2] weig[S[i][1]] = S[i][0] return self.__htable_to_list__(parent,weig,endVertex)
def __getDFSParentHashTable__(self,vertex): vertex = self.dtype(vertex) parent = ht.HashTable() parent[vertex] = None [ next_vert for next_vert in self.__DFS_Vertex__(vertex,parent)] return parent
def addNewVertex(self,vertex): vertex = self.dtype(vertex) if(self._hashTable_[vertex] == None): self.noVertices += 1 self._hashTable_[vertex] = ht.HashTable()