この記事は、pythonで実装しているということ、グラフがWikipediaからとってきたものであるという以外、繋がりを可視化する グラフ理論入門のほぼコピーです。
また、ほぼ同じような内容で下記もとても易しく理解できます。
グラフ
グラフとは、関係を可視化するデータ構造です。
上のグラフでいうと、1から6の数字のことを頂点(vertex)または節点(node)、頂点と頂点の結びつきを辺(edge)と呼びます。また、edgeに方向性のあるものを有向グラフ、方向性のないものを無向グラフと呼びます。
グラフを辞書で表現
グラフのデータ構造
class Graph():
def __init__(self):
""" ノードのつながりを辞書型で表現する """
self.adjacency_dict = {}
def add_vertex(self, v):
""" ノードを追加する """
self.adjacency_dict[v] = []
def add_edge(self, v1, v2):
""" ノード同士をつなぐ。"""
# 無向グラフの場合は双方向。もし有向グラフなら片側のみ。
self.adjacency_dict[v1].append(v2)
self.adjacency_dict[v2].append(v1)
def remove_edge(self, v1, v2):
""" ノード同士のつながりを削除する。"""
self.adjacency_dict[v1].remove(v2)
self.adjacency_dict[v2].remove(v1)
def remove_vertex(self,v):
""" ノードを削除する。"""
while self.adjacency_dict[v] != []:
adjacent_vertex = self.adjacency_dict[v][-1]
self.remove_edge(v, adjacent_vertex)
del self.adjacency_dict[v]
def print_graph(self):
print(self.adjacency_dict)
グラフのノードとエッジを設定
下のグラフを設定します。
graph = Graph() graph.add_vertex(1) graph.add_vertex(2) graph.add_vertex(3) graph.add_vertex(4) graph.add_vertex(5) graph.add_vertex(6) graph.add_edge(1, 2) graph.add_edge(1, 5) graph.add_edge(2, 3) graph.add_edge(2, 5) graph.add_edge(3, 4) graph.add_edge(4, 5) graph.add_edge(4, 6) graph.print_graph()
以下のように出力されます。
{1: [2, 5], 2: [1, 3, 5], 3: [2, 4], 4: [3, 5, 6], 5: [1, 2, 4], 6: [4]}
グラフの探索
深さ優先探索
スタックを使う場合
スタックを使ってLIFOで探索を行います。
def dfs_stack(self, start, visited=None):
""" 深さ優先探索 Depth First Search スタックバージョン"""
# ノードを格納するスタック
stack = [start]
# 訪れた順番を格納
visited = []
while stack != []:
current_vertex = stack.pop()
visited.append(current_vertex)
for v in self.adjacency_dict[current_vertex]:
if v not in visited and v not in stack:
stack.append(v)
print(visited)
1から探索を行います。
graph.dfs_stack(1)
探索を行った順番です。
[1, 5, 4, 6, 3, 2]
再帰を使う場合
def dfs_rec(self, start, visited=None):
""" 深さ優先探索 Depth First Search 再帰バージョン"""
if visited is None:
visited = []
visited.append(start)
print(start)
for v in self.adjacency_dict[start]:
if v in visited:
continue
self.dfs_rec(v, visited)
1から探索を行う場合
graph.dfs_rec(1)
探索を行った順番です。
[1] [1, 2] [1, 2, 3] [1, 2, 3, 4] [1, 2, 3, 4, 5] [1, 2, 3, 4, 5, 6]
幅優先探索
キューを使ったFIFOで探索を行います。
def bfs(self, start, visited=None):
# ノードを格納するキュー
queue = collections.deque([start])
# 訪れた順番を格納
visited = []
while list(queue) != []:
current_vertex = queue.popleft()
visited.append(current_vertex)
for v in self.adjacency_dict[current_vertex]:
if v not in visited and v not in queue:
queue.append(v)
print(visited)
1から探索を行う場合
graph.bfs(1)
探索を行った順番です。
[1, 2, 5, 3, 4, 6]