無向グラフをDFSで探索するアルゴリズムを Python で記述します。
グラフは隣接リストを用いて表現します。
深さ優先探索
深さ優先探索(ふかさゆうせんたんさく、英: depth-first search, DFS、バックトラック法ともいう)は、木やグラフを探索するためのアルゴリズムである。
出典: フリー百科事典『ウィキペディア(Wikipedia)』
Wikipedia には、スタックを用いる疑似コードと再帰を用いる疑似コードが載っています。
ここでは、再帰を用いて実装します。
以下のようなグラフの探索を行います。
以前の隣接リストのコードに以下を追記しています。
- 6行目でVertex クラスに visited という訪問済みかどうかのフラグを設定
- 24行目に visited を設定する関数
- 33行目でGraph クラスに
__iter__
- 70行目、77行目に DFS の関数
class Vertex(object): def __init__(self, id): self.id = id self.adjacent = {} # DFS self.visited = False def add_neighbor(self, neighbor, weight=0): self.adjacent[neighbor] = weight def get_neighbors(self): return self.adjacent def get_connections(self): return self.adjacent.keys() def get_vertex_id(self): return self.id def get_weight(self, neighbor): return self.adjacent[neighbor] # DFS def set_visited(self): self.visited = True class Graph(object): def __init__(self): self.vertex_dict = {} self.num_vertex = 0 # DFS ループで扱いやすくする。 def __iter__(self): return iter(self.vertex_dict.values()) def add_vertex(self, id): self.num_vertex = self.num_vertex + 1 new_vertex = Vertex(id) self.vertex_dict[id] = new_vertex return new_vertex def get_vertex(self, id): if id in self.vertex_dict: return self.vertex_dict[id] else: return None def add_edge(self, frm, to, weight=0): if frm not in self.vertex_dict: self.add_vertex(frm) if to not in self.vertex_dict: self.add_vertex(to) self.vertex_dict[frm].add_neighbor(self.vertex_dict[to], weight) # 有向グラフの場合は別 self.vertex_dict[to].add_neighbor(self.vertex_dict[frm], weight) def get_vertices(self): return self.vertex_dict.keys() def get_edges(self): edges = [] for v in self.vertex_dict.values(): for w in v.get_connections(): vid = v.get_vertex_id() wid = w.get_vertex_id() edges.append((vid, wid, v.get_weight(w))) return edges def dfs(graph, current_vertex, traversal_verticies): current_vertex.set_visited() traversal_verticies.append(current_vertex.get_vertex_id()) for neighbor in current_vertex.get_connections(): if not(neighbor.visited): dfs(graph, neighbor, traversal_verticies) def dfs_traversal(graph): for current_vertex in graph: traversal_verticies = [] if not(current_vertex.visited): dfs(graph, current_vertex, traversal_verticies) if traversal_verticies: print(f'start from {current_vertex.get_vertex_id()}:', traversal_verticies) if __name__ == '__main__': graph = Graph() graph.add_vertex('a') graph.add_vertex('b') graph.add_vertex('c') graph.add_vertex('d') graph.add_vertex('e') graph.add_vertex('f') graph.add_edge('a', 'b', 1) graph.add_edge('a', 'c', 1) graph.add_edge('a', 'e', 1) graph.add_edge('b', 'd', 1) graph.add_edge('b', 'e', 1) graph.add_edge('c', 'd', 1) graph.add_edge('c', 'e', 1) graph.add_edge('d', 'e', 1) dfs_traversal(graph) # start from a: ['a', 'b', 'd', 'c', 'e'] # start from f: ['f']
想定通りの結果を得ることができました。