그래프의 각 정점을 방문하는 그래프 순회 (Graph Traversals)에는 크게 DFS, BFS 의 2가지 방법이 있음
다음과 같이 그래프를 인접리스트를 사용하여 표현
graph = {1: [2, 3, 4], 2: [5], 3: [5], 5: [6, 7], 7: [3]})
from collections import defaultdict, deque
class Graph:
def __init__(self, edge_list):
self.graph = defaultdict(list)
for v1, v2 in edge_list:
self.graph[v1].append(v2)
if __name__ == '__main__':
g = Graph([[1, 2], [1, 3], [1, 4], [2, 5], [3, 5], [5, 6], [5, 7], [7, 3]])
print(g.graph) #defaultdict(<class 'list'>, {1: [2, 3, 4], 2: [5], 3: [5], 5: [6, 7], 7: [3]})
임의의 노드에서 출발하여 해당 branch의 리프노드가 나올때까지 탐색 후, 다음 brunch로 넘어가서 반복하여 모든 정점을 탐색하는 방법
from collections import defaultdict, deque
class Graph:
def __init__(self, edge_list):
self.graph = defaultdict(list)
for v1, v2 in edge_list:
self.graph[v1].append(v2)
#DFS stack구현
def dfs_stack(self, start_v = 1):
visited = []
stack = [start_v]
while stack:
node = stack.pop()
if node not in visited:
visited.append(node)
stack.extend(self.graph[node])
return visited
if __name__ == '__main__':
g = Graph([[1, 2], [1, 3], [1, 4], [2, 5], [3, 5], [5, 6], [5, 7], [7, 3]])
print(g.graph) #defaultdict(<class 'list'>, {1: [2, 3, 4], 2: [5], 3: [5], 5: [6, 7], 7: [3]})
print(g.dfs_stack()) #[1, 4, 3, 5, 7, 6, 2]
stack구현과 같은 방법이지만 반복문(while stack:)을 사용하지 않고 재귀를 사용하여 구현.
from collections import defaultdict, deque
class Graph:
def __init__(self, edge_list):
self.graph = defaultdict(list)
for v1, v2 in edge_list:
self.graph[v1].append(v2)
#DFS recursion구현
def dfs_recursion(self, node = 1, visited = []):
visited.append(node)
for v in self.graph[node]:
if v not in visited:
self.dfs_recursion(v, visited)
return visited
if __name__ == '__main__':
g = Graph([[1, 2], [1, 3], [1, 4], [2, 5], [3, 5], [5, 6], [5, 7], [7, 3]])
print(g.graph) #defaultdict(<class 'list'>, {1: [2, 3, 4], 2: [5], 3: [5], 5: [6, 7], 7: [3]})
print(g.dfs_recursion()) #[1, 2, 5, 6, 7, 3, 4]
print(g.dfs_stack()) #stack 구현 출력: [1, 4, 3, 5, 7, 6, 2]
print(g.dfs_recursion()) #recursion 구현 출력[1, 2, 5, 6, 7, 3, 4]
dfs_recursion은 child node를 사전식 순서대로 방문 하지만 dfs_stack에서 stack은 LIFO구조 이므로 node를 사전식 순서의 역순으로 방문하기 때문
임의의 노드에서 출발하여 해당 level의 정점들을 모두 탐색한 뒤, 그 다음 level을 탐색하는 것을 반복하여 모든 정점들을 탐색
from collections import defaultdict, deque
class Graph:
def __init__(self, edge_list):
self.graph = defaultdict(list)
for v1, v2 in edge_list:
self.graph[v1].append(v2)
#BFS queue구현
def bfs_queue(self, start_v = 1):
visited = [start_v]
q = deque([start_v])
while q:
node = q.popleft()
for v in self.graph[node]:
if v not in visited:
q.append(v)
visited.append(v)
return visited
if __name__ == '__main__':
g = Graph([[1, 2], [1, 3], [1, 4], [2, 5], [3, 5], [5, 6], [5, 7], [7, 3]])
print(g.graph) #defaultdict(<class 'list'>, {1: [2, 3, 4], 2: [5], 3: [5], 5: [6, 7], 7: [3]})
print(g.bfs_queue()) #[1, 2, 3, 4, 5, 6, 7]
from collections import defaultdict, deque
class Graph:
def __init__(self, edge_list):
self.graph = defaultdict(list)
for v1, v2 in edge_list:
self.graph[v1].append(v2)
#DFS stack구현
def dfs_stack(self, start_v = 1):
visited = []
stack = [start_v]
while stack:
node = stack.pop()
if node not in visited:
visited.append(node)
stack.extend(self.graph[node])
return visited
#DFS recursion구현
def dfs_recursion(self, node = 1, visited = []):
visited.append(node)
for v in self.graph[node]:
if v not in visited:
self.dfs_recursion(v, visited)
return visited
#BFS queue구현
def bfs_queue(self, start_v = 1):
visited = [start_v]
q = deque([start_v])
while q:
node = q.popleft()
for v in self.graph[node]:
if v not in visited:
q.append(v)
visited.append(v)
return visited
if __name__ == '__main__':
g = Graph([[1, 2], [1, 3], [1, 4], [2, 5], [3, 5], [5, 6], [5, 7], [7, 3]])
print(g.graph) #defaultdict(<class 'list'>, {1: [2, 3, 4], 2: [5], 3: [5], 5: [6, 7], 7: [3]})
print(g.dfs_stack()) #[1, 4, 3, 5, 7, 6, 2]
print(g.dfs_recursion()) #[1, 2, 5, 6, 7, 3, 4]
print(g.bfs_queue()) #[1, 2, 3, 4, 5, 6, 7]
DFS : 탐색한 정점인지 아닌지 확인
BFS: queue에 넣었던 정점인지 아닌지 확인( = 탐색한 정점인지 아닌지 확인)
DFS는 stack자료구조를 사용하기 때문에 stack에 들어간 데이터들의 순서와 탐색한 데이터들의 순서가 다르다. 데이터를 꺼낼 때, 그 데이터를 탐색했다고 생각하면 이해가 편함.
BFS는 queue자료구조를 사용하기 때문에 queue에 들어간 데이터들의 순서와 탐색한 데이터들의 순서가 같다. queue에 데이터를 넣을 때, 그 데이터를 탐색했다고 생각하면 이해가 편함