def keys_between(self, lo, hi): """Returns all keys in this symbol table in the given range, as an Iterable. :param lo: minimum endpoint :param hi: maximum endpoint :returns: all keys in this symbol table between lo (inclusive) and hi (inclusive) :raises ValueError: if either lo or hi are None """ if lo is None: raise ValueError("first argument to keys() is None") if hi is None: raise ValueError("second argument to keys() is None") queue = Queue() if lo > hi: return queue i = self.rank(lo) end = self.rank(hi) while i < end: queue.enqueue(self._keys[i]) i += 1 if self.contains(hi): queue.enqueue(self._keys[self.rank(hi)]) return queue
def diameter(G): marked = [False] * G.V() edge_to = [None] * G.V() q = Queue() marked[0] = True q.enqueue(0) while not q.is_empty(): v = q.dequeue() for adj in G.adj(v): if marked[adj] == False: marked[adj] = True edge_to[adj] = v q.enqueue(adj) # compute dist of all paths paths = [] for i in range(G.V()): dist = 0 curr = i while edge_to[curr] != None: dist += 1 curr = edge_to[curr] paths.append(dist) return max(paths)
class DepthFirstOrder: def __init__(self, G): self._G = G self._pre = Queue() self._post = Queue() self._reverse_post = Stack() self._marked = [False] * G.V() for v in range(G.V()): if not self._marked[v]: self.dfs(v) def dfs(self, v): self._pre.enqueue(v) self._marked[v] = True for w in self._G.adj(v): if not self._marked[w]: self.dfs(w) self._post.enqueue(v) self._reverse_post.push(v) def pre(self): return self._pre def post(self): return self._post def reverse_post(self): return self._reverse_post
class KruskalMST: """The KruskalMST class represents a data type for computing a minimum spanning tree in an edge-weighted graph. The edge weights can be positive, zero, or negative and need not be distinct. If the graph is not connected, it computes a minimum spanning forest, which is the union of minimum spanning trees in each connected component. The weight method returns the weight of a minimum spanning tree and the edges method returns its edges. This implementation uses Kruskal's algorithm and the union-find data type. The constructor takes time proportional to E log E and extra space (not including the graph) proportional to V, where V is the number of vertices and E is the number of edges- Afterwards, the weight method takes constant time and the edges method takes time proportional to V. """ def __init__(self, G): """Computes a minimum spanning tree (or forest) of an edge-weighted graph. :param G: the edge-weighted graph """ self._weight = 0 self._mst = Queue() pq = MinPQ() for e in G.edges(): pq.insert(e) uf = WeightedQuickUnionUF(G.V()) while not pq.is_empty() and self._mst.size() < G.V() - 1: e = pq.del_min() v = e.either() w = e.other(v) if not uf.connected(v, w): uf.union(v, w) self._mst.enqueue(e) self._weight += e.weight() def edges(self): """Returns the edges in a minimum spanning tree (or forest). :return: the edges in a minimum spanning tree (or forest) """ return self._mst def weight(self): """Returns the sum of the edge weights in a minimum spanning tree (or forest). :return: the sum of the edge weights in a minimum spanning tree (or forest) """ return self._weight
def keys_with_prefix(self, prefix): if prefix is None: raise ValueError("calls keys_with_prefix with null argument") # TODO IllegalArgumentException queue = Queue() x = self._get(self.root, prefix, 0) if x is None: return queue if x.val is not None: queue.enqueue(prefix) self._collect(x.mid, prefix, queue) return queue
def _bfs(self, G, s): # breadth-first search from a single source queue = Queue() self._marked[s] = True # Mark the source queue.enqueue(s) # and put it on the queue. while not queue.is_empty(): v = queue.dequeue() # Remove next vertex from the queue. for w in G.adj(v): if not self._marked[w]: self._edgeTo[w] = v # For every unmarked adjacent vertex, self._marked[w] = True # mark it because path is known, queue.enqueue(w) # and add it to the queue.
def edges(self): """ Returns the edges in a minimum spanning tree (or forest). :returns: the edges in a minimum spanning tree (or forest) as an iterable of edges """ mst = Queue() for v in range(len(self._edge_to)): e = self._edge_to[v] if e is not None: mst.enqueue(e) return mst
def bfs(self, G, s): # mark source node and insert into queue to initialise bfs self._marked[s] = True q = Queue() q.enqueue(s) # continue bfs until queue is empty (all reachable nodes have been visited) while q.is_empty() == False: s = q.dequeue() for adj in G.adj(s): if self._marked[adj] == False: self._marked[adj] = True q.enqueue(adj) self._count += 1
def can_be_colored(self, src): coloring = [-1] * self._V for src in range(self._V): if coloring[src] == -1: coloring[src] = 1 # Create a queue (FIFO) of vertex numbers and # enqueue source vertex for BFS traversal q = Queue() q.enqueue(src) # Run while there are vertices in queue # (Similar to BFS) while q.is_empty() == False: u = q.dequeue() for edge in self.adj(u): start = u for possibility in [edge.start(), edge.end()]: if possibility != u: end = possibility # adjacent not visited yet if coloring[end] == -1: if edge.type() == 0: # harmony coloring[end] = coloring[start] elif edge.type() == 1: # conflict if coloring[start] == 0: coloring[end] = 1 elif coloring[start] == 1: coloring[end] = 0 q.enqueue(end) continue if edge.type() == 0: # harmony # print(coloring[edge.start()], coloring[edge.end()]) if coloring[start] != coloring[end]: return 0 elif edge.type() == 1: # conflict # print(coloring[edge.start()], coloring[edge.end()]) if coloring[start] == coloring[end]: return 0 return 1
class LazyPrimMST: def __init__(self, G): self._weight = 0.0 # total weight of MST self._mst = Queue() # edges in the MST self._marked = [False] * G.V() # marked[v] = True if v on tree self._pq = MinPQ() # edges with one endpoint in tree for v in range(G.V()): # run Prim from all vertices to if not self._marked[v]: self._prim(G, v) # get a minimum spanning forest def _prim(self, G, s): # run prim's algorithm self._scan(G, s) while not self._pq.is_empty(): # better to stop when mst has V-1 edges e = self._pq.del_min() # smallest edge on pq (frontier) v = e.either() # two endpoints w = e.other(v) assert self._marked[v] or self._marked[w] if (self._marked[v] and self._marked[w]): # lazy, both v and w already scanned continue self._mst.enqueue(e) # add e to MST self._weight += e.weight() if not self._marked[v]: self._scan(G, v) # v becomes part of tree if not self._marked[w]: self._scan(G, w) # w becomes part of tree def _scan(self, G, v): # add all edges e incident to v onto pq if the other endpoint has not yet been scanned assert not self._marked[v] self._marked[v] = True for e in G.adj(v): if not self._marked[e.other(v)]: self._pq.insert(e) def edges(self): return self._mst def weight(self): return self._weight
def solve(world, R, L): # find starting indices start_from = [] for j in range(R): if world[j][0] == 'P': start_from.append((j, 1)) # dijkstra shortest path finding from starting positions to the right min_distances = [] for index in start_from: i, j = index # built dist_to array dist_to = [] for _ in range(R): line = [] for _ in range(L): line.append(math.inf) dist_to.append(line) # intialise starting position dist_to[i][j] = int(world[i][j]) q = Queue() q.enqueue((i, j)) while not q.is_empty(): v = q.dequeue() # dequeue current index for adj in adjacents(R, L, v[0], v[1]): i, j = adj[0], adj[1] if world[i][j] in ['P', 'U', 'M', 'A', '.']: # print(f'found: {world[i][j]}') continue else: # if distance can be decreased, decrease if dist_to[i][j] > int(world[i][j]) + dist_to[v[0]][v[1]]: dist_to[i][j] = int(world[i][j]) + dist_to[v[0]][v[1]] q.enqueue(adj) min_distances.append(min([dist_to[i][L-2] for i in range(R)])) return min(min_distances)
def _bfs(self, G: Graph, s: int): """ bfs to search for shortest path from source s to all vertex :param G: :param s: :return: """ queue = Queue() self._dist_to[s] = 0 self._edgeTo[s] = 0 self._marked[s] = True queue.enqueue(s) while not queue.is_empty(): v = queue.dequeue() for w in G.adj(v): if not self._marked[w]: self._marked[w] = True self._edgeTo[w] = v self._dist_to[w] = self._dist_to[v] + 1 queue.enqueue(w)
def build_fail(self): """ build the fail pointer for each node. fail pointer is the longest suffix which can match the other's prefix,最长可匹配后缀 and the pointer points to the prefix's last node. the building idea is like reversion. 1.pc(p's child)'s fail is qc: pc=qc when pc.data==qc.data. q =p.fail 2.root's fail is none BFS the AcTrie and build fail for each node get from queue. :return: """ queue = Queue() self._root.fail = None queue.enqueue(self._root) while not queue.is_empty(): p = queue.dequeue() # BFS traverse the trie,each time set the p'c child pc's fail when p's fail is known. for pc in p.children: if pc is None: continue if pc == self._root: pc.fail = self._root else: q = p.fail while q: # qc is the q'child which has same char with pc. qc = q.children[ord(pc.data)] if qc: pc.fail = qc break # if qc not exist, q=q.fail, repeat. q = q.fail if q is None: pc.fail = self._root queue.enqueue(pc)
def solve(R, C, world): flat_world = [item for sublist in world for item in sublist] G = Graph(len(flat_world)) # add horizontal edges for i in range(R): for j in range(C - 1): G.add_edge(j + (i * C), j + (i * C) + 1) # add vertical edges for i in range(R - 1): for j in range(C): G.add_edge(j + (i * C), j + (i * C) + C) # create mapping mapping = {i: flat_world[i] for i in range(len(flat_world))} # print(G) # print(mapping) # find virus and make it source s = 0 for x in flat_world: if x == 2: break s += 1 # start bfs from infection q = Queue() q.enqueue(s) while q.is_empty() == False: v = q.dequeue() for adj in G.adj(v): if mapping[adj] == 1: # land mapping[adj] = 2 # infect the land q.enqueue(adj) elif mapping[adj] == 3: # encounter human return 1 return 0
class KruskalMST: def __init__(self, G): # initialise self._mst = Queue() self._edges = MinPQ() for edge in G.edges(): self._edges.insert(edge) self._uf = WeightedQuickUnionUF(G.V()) while not self._edges.is_empty() and self._mst.size() < G.V() - 1: min_edge = self._edges.del_min() u = min_edge.either() v = min_edge.other(u) if self._uf.connected(u, v): continue # ineligible edge self._uf.union(u, v) # union (are part of mst now) self._mst.enqueue(min_edge) def edges(self): return self._mst def weight(self): return sum([edge.weight() for edge in self._mst])
def shortest_path(G, u, v): marked = [False] * G.V() edge_to = [None] * G.V() q = Queue() marked[u] = True q.enqueue(u) while not q.is_empty(): s = q.dequeue() for adj in G.adj(s): if not marked[adj]: marked[s] = True edge_to[adj] = s q.enqueue(adj) dist = 0 curr = v while edge_to[curr] != None: dist += 1 curr = edge_to[curr] return dist
def solve(R, C, world): # find virus and make it source s = None for i in range(R): for j in range(C): if world[i][j] == 2: s = (i, j) # start bfs from infection q = Queue() q.enqueue(s) while q.is_empty() == False: v = q.dequeue() # for row in world: # print(row) # print('\n') for adj in adjacents(world, v[0], v[1]): if world[adj[0]][adj[1]] == 1: world[adj[0]][adj[1]] = 2 q.enqueue(adj) elif world[adj[0]][adj[1]] == 3: return 1 return 0
def connected_inhabitans_multiworld(L): # intialise graph object with V vertices R = len(L[0]) C = len(L) flat_L = [item for sublist in L for item in sublist] V = len(flat_L) G = AdjacencyListGraph(V) # add horizontal edges for i in range(R - 1): for j in range(C): G.add_edge(i + (R * j), i + (R * j) + 1) # add right diagonal edges for i in range(R): for j in range(C - 1): if j % 2 == 0: G.add_edge(i + (R * j), i + (R * j) + R) else: if i >= R - 1: break G.add_edge(i + (R * j), i + (R * j) + R + 1) # add left diagonal edges for i in range(R): for j in range(C - 1): if j % 2 == 0: if i == 0: continue G.add_edge(i + (R * j), i + (R * j) + R - 1) else: G.add_edge(i + (R * j), i + (R * j) + R) print(G) # create mapping mapping = {i: flat_L[i] for i in range(len(flat_L))} print(mapping) # find source s = 0 for x in flat_L: if x == 0: break s += 1 d = 0 while True: print(d, mapping) # total number of grass fields total_count = len([x for x in mapping.values() if x == 0]) marked = [False] * G.V() q = Queue() count = 1 marked[s] = True q.enqueue(s) while q.is_empty() == False: v = q.dequeue() for adj in G.adj(v): if marked[adj] == False and mapping[adj] == 0: marked[adj] = True q.enqueue(adj) count += 1 print(total_count, count) if count == total_count: return d # base case (no more lowering of water possible) if len([x for x in mapping.values() if x > 0]) == 0: return 'impossible' d += 1 for i in range(len(mapping)): if mapping[i] > 0: mapping[i] -= 1
class LazyPrimMST: """The LazyPrimMST class represents a data type for computing a minimum spanning tree in an edge-weighted graph. The edge weights can be positive, zero, or negative and need not be distinct. If the graph is not connected, it computes a minimum spanning forest, which is the union of minimum spanning trees in each connected component. The weight() method returns the weight of a minimum spanning tree and the edges() method returns its edges. This implementation uses a lazy version of Prim's algorithm with a binary heap of edges. The constructor takes time proportional to E log E and extra space (not including the graph) proportional to E, where V is the number of vertices and E is the number of edges. Afterwards, the weight() method takes constant time and the edges() method takes time proportional to V. """ FLOATING_POINT_EPSILON = 1e-12 def __init__(self, G): """Compute a minimum spanning tree (or forest) of an edge-weighted graph. :param G: the edge-weighted graph """ self._weight = 0.0 # total weight of MST self._mst = Queue() # edges in the MST self._marked = [False] * G.V() # marked[v] = True if v on tree self._pq = MinPQ() # edges with one endpoint in tree for v in range(G.V()): # run Prim from all vertices to if not self._marked[v]: self._prim(G, v) # get a minimum spanning forest # check optimality conditions assert self._check(G) def _prim(self, G, s): # run Prim's algorithm self._scan(G, s) while not self._pq.is_empty(): # better to stop when mst has V-1 edges e = self._pq.del_min() # smallest edge on pq v = e.either() # two endpoints w = e.other(v) assert self._marked[v] or self._marked[w] if (self._marked[v] and self._marked[w]): # lazy, both v and w already scanned continue self._mst.enqueue(e) # add e to MST self._weight += e.weight() if not self._marked[v]: self._scan(G, v) # v becomes part of tree if not self._marked[w]: self._scan(G, w) # w becomes part of tree def _scan(self, G, v): # add all edges e incident to v onto pq if the other endpoint has not yet been scanned assert not self._marked[v] self._marked[v] = True for e in G.adj(v): if not self._marked[e.other(v)]: self._pq.insert(e) def edges(self): """Returns the edges in a minimum spanning tree (or forest). :returns: the edges in a minimum spanning tree (or forest) as an iterable of edges """ return self._mst def weight(self): """Returns the sum of the edge weights in a minimum spanning tree (or forest). :returns: the sum of the edge weights in a minimum spanning tree (or forest) """ return self._weight def _check(self, G): # check optimality conditions (takes time proportional to E V lg* V) totalWeight = 0.0 # check weight for e in self.edges(): totalWeight += e.weight() if abs(totalWeight - self.weight()) > LazyPrimMST.FLOATING_POINT_EPSILON: error = "Weight of edges does not equal weight(): {} vs. {}\n".format( totalWeight, self.weight()) print(error, file=sys.stderr) return False # check that it is acyclic uf = UF(G.V()) for e in self.edges(): v = e.either() w = e.other(v) if uf.connected(v, w): print("Not a forest", file=sys.stderr) return False uf.union(v, w) # check that it is a spanning forest for e in G.edges(): v = e.either() w = e.other(v) if not uf.connected(v, w): print("Not a forest", file=sys.stderr) return False # check that it is a minimal spanning forest (cut optimality conditions) for e in self.edges(): # all edges in MST except e uf = UF(G.V()) for f in self._mst: x = f.either() y = f.other(x) if f != e: uf.union(x, y) # check that e is min weight edge in crossing cut for f in G.edges(): x = f.either() y = f.other(x) if not uf.connected(x, y): if f.weight() < e.weight(): error = "Edge {} violates cut optimality conditions".format( f) print(error, file=sys.stderr) return False return True
class DepthFirstOrder: """The DepthFirstOrder class represents a data type for determining depth- first search ordering of the vertices in a digraph or edge-weighted digraph, including preorder, postorder, and reverse postorder. This implementation uses depth-first search. The constructor takes time proportional to V + E (in the worst case), where V is the number of vertices and E is the number of edges. Afterwards, the preorder, postorder, and reverse postorder operation takes take time proportional to V. For additional documentation, see Section 4.2 of Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. """ def __init__(self, digraph): """Determines a depth-first order for the digraph. :param digraph: the digraph to check """ self._pre = [0] * digraph.V() self._post = [0] * digraph.V() self._preorder = Queue() self._postorder = Queue() self._marked = [False] * digraph.V() self._pre_counter = 0 self._post_counter = 0 if isinstance(digraph, Digraph): dfs = self._dfs else: dfs = self._dfs_edge_weighted for v in range(digraph.V()): if not self._marked[v]: dfs(digraph, v) def post(self, v=None): """Either returns the postorder number of vertex v or, if v is None, returns the vertices in postorder. :param v: None, or the vertex to return the postorder number of :return: if v is None, the vertices in postorder, otherwise the postorder number of v """ if v is None: return self._postorder else: self._validate_vertex(v) return self._post[v] def pre(self, v=None): """Either returns the preorder number of vertex v or, if v is None, returns the vertices in preorder. :param v: None, or the vertex to return the preorder number of :return: if v is None, the vertices in preorder, otherwise the preorder number of v """ if v is None: return self._preorder else: self._validate_vertex(v) return self._pre[v] def reverse_post(self): """Returns the vertices in reverse postorder. :return: the vertices in reverse postorder, as an iterable of vertices """ reverse = Stack() for v in self._postorder: reverse.push(v) return reverse # run DFS in digraph G from vertex v and compute preorder/postorder def _dfs(self, digraph, v): self._marked[v] = True self._pre[v] = self._pre_counter self._pre_counter += 1 self._preorder.enqueue(v) for w in digraph.adj(v): if not self._marked[w]: self._dfs(digraph, w) self._postorder.enqueue(v) self._post[v] = self._post_counter self._post_counter += 1 # run DFS in edge-weighted digraph G from vertex v and compute preorder/postorder def _dfs_edge_weighted(self, graph, v): self._marked[v] = True self._pre[v] = self._pre_counter self._pre_counter += 1 self._preorder.enqueue(v) for edge in graph.adj(v): w = edge.to_vertex() if not self._marked[w]: self._dfs_edge_weighted(graph, w) self._postorder.enqueue(v) self._post[v] = self._post_counter self._post_counter += 1 # throw an IllegalArgumentException unless 0 <= v < V def _validate_vertex(self, v): V = len(self._marked) if v < 0 or v >= V: raise ValueError("vertex {} is not between 0 and {}", v, V - 1) # check that pre() and post() are consistent with pre(v) and post(v) def _check(self): # check that post(v) is consistent with post() r = 0 for v in self.post(): if self.post(v) != r: print("post(v) and post() inconsistent") return False r += 1 # check that pre(v) is consistent with pre() r = 0 for v in self.pre(): if self.pre(v) != r: print("pre(v) and pre() inconsistent") return False r += 1 return True
# Created for BADS 2018 # See README.md for details # Python 3 import sys from itu.algs4.fundamentals.queue import Queue from itu.algs4.stdlib import stdio # is this really useful?? try: q = Queue() q.enqueue(1) except AttributeError: print("ERROR - Could not import itu.algs4 queue") sys.exit(1) # Execution: python lookup_index.py movies.txt "/" # Dependencies: queue.py stdio.py # % python lookup_index.py aminoI.csv "," # Serine # TCT # TCA # TCG # AGT # AGC # TCG # Serine # # % python lookup_index.py movies.txt "/" # Bacon, Kevin # Animal House (1978)
class BellmanFordSP: # Computes a shortest paths tree from s to every other vertex in # the edge-weighted digraph G. # @param G the acyclic digraph # @param s the source vertex # @throws IllegalArgumentException unless 0 <= s < V def __init__(self, G, s): self._distTo = [sys.float_info.max ] * G.V() #distTo[v] = distance of shortest s->v path self._edgeTo = [None ] * G.V() #edgeTo[v] = last edge on shortest s->v path self._onQueue = [False ] * G.V() #onQueue[v] = is v currently on the queue? self._queue = Queue() #queue of vertices to relax self._cost = 0 #number of calls to relax() self._cycle = None #negative cycle (or None if no such cycle) #Bellman-Ford algorithm self._distTo[s] = 0.0 self._queue.enqueue(s) self._onQueue[s] = True while not self._queue.is_empty() and not self.has_negative_cycle(): v = self._queue.dequeue() self._onQueue[v] = False self._relax(G, v) assert self._check(G, s) #relax vertex v and put other endpoints on queue if changed def _relax(self, G, v): for e in G.adj(v): w = e.to_vertex() if self._distTo[w] > self._distTo[v] + e.weight(): self._distTo[w] = self._distTo[v] + e.weight() self._edgeTo[w] = e if not self._onQueue[w]: self._queue.enqueue(w) self._onQueue[w] = True if self._cost % G.V() == 0: self._find_negative_cycle() if self.has_negative_cycle(): return #found a negative cycle self._cost += 1 # Is there a negative cycle reachable from the source vertex s? # @return true if there is a negative cycle reachable from the # source vertex s, and false otherwise def has_negative_cycle(self): return self._cycle is not None # Returns a negative cycle reachable from the source vertex s, or None # if there is no such cycle. # @return a negative cycle reachable from the soruce vertex s # as an iterable of edges, and None if there is no such cycle def negative_cycle(self): return self._cycle #by finding a cycle in predecessor graph def _find_negative_cycle(self): V = len(self._edgeTo) spt = EdgeWeightedDigraph(V) for v in range(V): if self._edgeTo[v] is not None: spt.add_edge(self._edgeTo[v]) finder = EdgeWeightedDirectedCycle(spt) self._cycle = finder.cycle() # Returns the length of a shortest path from the source vertex s to vertex v. # @param v the destination vertex # @return the length of a shortest path from the source vertex s to vertex v; # sys.float_info.max if no such path # @throws UnsupportedOperationException if there is a negative cost cycle reachable # from the source vertex s # @throws IllegalArgumentException unless 0 <= v < V def dist_to(self, v): self._validate_vertex(v) if self.has_negative_cycle(): raise UnsupportedOperationException("Negative cost cycle exists") return self._distTo[v] # Is there a path from the source s to vertex v? # @param v the destination vertex # @return true if there is a path from the source vertex # s to vertex v, and false otherwise # @throws IllegalArgumentException unless 0 <= v < V def has_path_to(self, v): self._validate_vertex(v) return self._distTo[v] < sys.float_info.max # Returns a shortest path from the source s to vertex v. # @param v the destination vertex # @return a shortest path from the source s to vertex v # as an iterable of edges, and None if no such path # @throws UnsupportedOperationException if there is a negative cost cycle reachable # from the source vertex s # @throws IllegalArgumentException unless 0 <= v < V def path_to(self, v): self._validate_vertex(v) if self.has_negative_cycle(): raise UnsupportedOperationException("Negative cost cycle exists") if not self.has_path_to(v): return None path = Stack() e = self._edgeTo[v] while e is not None: path.push(e) e = self._edgeTo[e.from_vertex()] return path #check optimality conditions: either #(i) there exists a negative cycle reacheable from s # or #(ii) for all edges e = v->w: distTo[w] <= distTo[v] + e.weight() #(ii') for all edges e = v->w on the SPT: distTo[w] == distTo[v] + e.weight() def _check(self, G, s): #has a negative cycle if self.has_negative_cycle(): weight = 0.0 for e in self.negative_cycle(): weight += e.weight() if weight >= 0.0: print("error: weight of negative cycle = {}".format(weight)) return False #no negative cycle reachable from source else: #check that distTo[v] and edgeTo[v] are consistent if self._distTo[s] != 0.0 or self._edgeTo[s] is not None: print("distanceTo[s] and edgeTo[s] inconsistent") return False for v in range(G.V()): if v == s: continue if self._edgeTo[v] is None and self._distTo[ v] != sys.float_info.max: print("distTo[] and edgeTo[] inconsistent") return False #check that all edges e = v->w satisfy distTo[w] <= distTo[v] + e.weight() for v in range(G.V()): for e in G.adj(v): w = e.to_vertex() if self._distTo[v] + e.weight() < self._distTo[w]: print("edge {} not relaxed".format(e)) return False #check that all edges e = v->w on SPT satisfy distTo[w] == distTo[v] + e.weight() for w in range(G.V()): if self._edgeTo[w] is None: continue e = self._edgeTo[w] v = e.from_vertex() if w != e.to_vertex(): return False if self._distTo[v] + e.weight() != self._distTo[w]: print("edge {} on shortest path not tight".format(e)) return False print("Satisfies optimality conditions") print() return True #raise an IllegalArgumentException unless 0 <= v < V def _validate_vertex(self, v): V = len(self._distTo) if v < 0 or v >= V: raise IllegalArgumentException( "vertex {} is not between 0 and {}".format(v, V - 1))