def __init__(self, word_list): node_count = len(word_list) self.word_list = word_list self.graph = Graph(node_count) self.word_to_vertex_id = {} for i in xrange(node_count - 1): self.word_to_vertex_id[word_list[i]] = i for j in xrange(i + 1, node_count): word_distance = 0 for k in xrange(len(word_list[i])): if word_list[i][k] == word_list[j][k]: continue else: word_distance += 1 if word_distance > 1: break if word_distance == 1: # add to graph as they are adjacent nodes in the graph self.graph.add_edge(i, j)
class WordLadder(object): """ Assumes all words in input word_list are of same length, otherwise, innermost loop in constructor will degrade from O(m) to O(m^2) to compute levenshtein distances accounting for insertions and deletions as well. m = average number of letters per word """ def __init__(self, word_list): node_count = len(word_list) self.word_list = word_list self.graph = Graph(node_count) self.word_to_vertex_id = {} for i in xrange(node_count - 1): self.word_to_vertex_id[word_list[i]] = i for j in xrange(i + 1, node_count): word_distance = 0 for k in xrange(len(word_list[i])): if word_list[i][k] == word_list[j][k]: continue else: word_distance += 1 if word_distance > 1: break if word_distance == 1: # add to graph as they are adjacent nodes in the graph self.graph.add_edge(i, j) def word_ladder(self, w1, w2): i1 = self.word_to_vertex_id[w1] i2 = self.word_to_vertex_id[w2] bfs = BreadthFirstPaths(self.graph, i1) path = [] x = i2 while x != i1: path.append(self.word_list[x]) x = bfs.edge_to[x] path.append(self.word_list[x]) return path
def __init__(self, rows, cols, startx, starty, endx, endy): self.rows = rows self.cols = cols self.startx = startx self.starty = starty self.endx = endx self.endy = endy self.graph = Graph(rows * cols) self.visited = [[False for _ in xrange(cols)] for r in xrange(rows)] q = deque() q.append((startx, starty)) self.visited[startx][starty] = True while q: x, y = q.popleft() neighbors = self.next_knight_squares_from(x, y) for r, c in neighbors: self.graph.add_edge(self.get_vertex_id(x, y), self.get_vertex_id(r, c)) if not self.visited[r][c]: self.visited[r][c] = True q.append((r, c))
x = vertex while x != adj: try: x = self.prev_to[x] self.cycle.append(x) except KeyError: break self.cycle.append(adj) return if __name__ == '__main__': from graph_api import Graph g = Graph() g.add_edge(0) g.add_edge(1, 0) g.add_edge(2, 0) g.add_edge(6, 0) g.add_edge(5, 0) g.add_edge(3, 1) g.add_edge(3, 2) g.add_edge(4, 2) g.add_edge(4, 6) g.add_edge(5, 0) g.add_edge(5, 4) bp = BipartiteGraph(g) assert bp.is_bipartite
:param graph_obj: :param src: :return: """ self.dist_to[src] = 0 self._marked[src] = True q = deque() q.append(src) while q: v = q.popleft() for u in graph_obj.adj(v): if not self._marked[u]: self.dist_to[u] = self.dist_to[v] + 1 self.edge_to[u] = v self._marked[u] = True q.append(u) if __name__ == '__main__': l = [ 13, 13, (0, 5), (4, 3), (0, 1), (9, 12), (6, 4), (5, 4), (0, 2), (11, 12), (9, 10), (0, 6), (7, 8), (9, 11), (5, 3) ] g = Graph(l) bfp = BreadthFirstPaths(g, 6) print bfp.edge_to # [6, 0, 0, 4, 6, 0, None, None, None, None, None, None, None] print bfp._marked # [True, True, True, True, True, True, True, False, False, False, False, # False, False] print bfp.dist_to # [1, 2, 2, 2, 1, 2, 0, inf, inf, inf, inf, inf, inf] # shortest path array from src 6
self.graph = graph self.visited = [] self.components = dict() # components counter self.count = 0 do = DepthFirstOrder(graph) for v in do: if v not in self.visited: self.dfs(v) self.count += 1 if __name__ == '__main__': from graph_api import Graph g = Graph() g.add_edge(0) g.add_edge(1, 0) g.add_edge(2, 1) g.add_edge(3, 2) g.add_edge(4, 1) g.add_edge(5, 2) g.add_edge(6, 2) g.add_edge(7, 7) g.add_edge(8, 7) g.add_edge(9, 7) g.add_edge(10, 7) g.add_edge(11, 11) g.add_edge(12, 11) cc = ConnectedComponents(g)
try: vertex = self.prev_to[vertex] except KeyError: break path.append(vertex) return reversed(path) def distance(self, vertex): """ Returns the minimum number of hops to the vertex from the starting vertex """ return self.dist_to[vertex] if __name__ == '__main__': from graph_api import Graph g = Graph() g.add_edge(0) g.add_edge(1, 0) g.add_edge(2, 1) g.add_edge(3, 2) g.add_edge(4, 1) g.add_edge(5, 2) g.add_edge(6, 2) g.add_edge(7, 3) g.add_edge(8, 7) g.add_edge(9, 7) g.add_edge(10, 7) dfs = DepthFirstPaths(g, 0) assert list(dfs.path_to(10)) == [0, 1, 2, 3, 7, 10] bfs = BreadthFirstPaths(g, 0)
class Board(object): def __init__(self, rows, cols, startx, starty, endx, endy): self.rows = rows self.cols = cols self.startx = startx self.starty = starty self.endx = endx self.endy = endy self.graph = Graph(rows * cols) self.visited = [[False for _ in xrange(cols)] for r in xrange(rows)] q = deque() q.append((startx, starty)) self.visited[startx][starty] = True while q: x, y = q.popleft() neighbors = self.next_knight_squares_from(x, y) for r, c in neighbors: self.graph.add_edge(self.get_vertex_id(x, y), self.get_vertex_id(r, c)) if not self.visited[r][c]: self.visited[r][c] = True q.append((r, c)) def get_minimum_moves(self): """ this gets the minimum knight moves from (startx, starty) -> (endx, endy) :return: """ # short circuit and return if end_cell not reachable if not self.visited[self.endx][self.endy]: return -1 else: bfp = BreadthFirstPaths(self.graph, self.get_vertex_id(self.startx, self.starty)) return bfp.dist_to[self.get_vertex_id(self.endx, self.endy)] def next_knight_squares_from(self, curr_row, curr_col): """ :param curr_row: :param curr_col: :return: returns a list of valid (i,j) co-ordinates where a knight can move, given its starting co-ordinates as (curr_row, curr_col) on a rectangular board of dimension rows x cols """ next_moves = [(curr_row + 2, curr_col + 1), ([curr_row + 2, curr_col - 1]), (curr_row - 2, curr_col + 1), ([curr_row - 2, curr_col - 1]), (curr_row + 1, curr_col + 2), ([curr_row + 1, curr_col - 2]), (curr_row - 1, curr_col + 2), ([curr_row - 1, curr_col - 2])] valid_next_moves = filter(self.valid, [(i, j) for i, j in next_moves]) return valid_next_moves def valid(self, coordinates): curr_row, curr_col = coordinates[0], coordinates[1] if 0 <= curr_row < self.rows and 0 <= curr_col < self.cols: return curr_row, curr_col def get_vertex_id(self, i, j): """ returns the index in a 1-dimensional array-index that represents the graph nodes :param j: :param i: :param cols: :return: vertex_id which is an integer """ return self.cols * i + j
""" Topological sort of the DAG """ def _dfs(self, vertex): self.visited.append(vertex) # iterate over edges not vertices since it is a DAG for edge in self.graph.iter_adjacent(vertex): v = edge.vertex_to() if v not in self.visited: self._dfs(v) # before exiting dfs - add the vertex to the list self.reversed_post.append(vertex) if __name__ == '__main__': from graph_api import Graph g = Graph() g.add_edge(0) g.add_edge(5, 2) g.add_edge(6, 2) g.add_edge(1, 0) g.add_edge(4, 1) g.add_edge(7, 3) g.add_edge(8, 7) g.add_edge(2, 1) g.add_edge(3, 2) g.add_edge(9, 7) g.add_edge(10, 7) do = DepthFirstOrder(g) assert list(iter(g)) == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]