알고리즘 문제

[알고리즘/Python] 깊이우선탐색(DFS) & 너비우선탐색(BFS)

Everyday Happy ❤︎ 2023. 12. 13. 09:00

🌳깊이우선탐색(DFS, Depth-Fist Search)🌳

출처:http://developer-mac.tistory.com/64

  • 최대한 깊이 내려간 뒤, 더 이상 깊이 갈 곳이 없을 경우 옆으로 이동
  • 루트 노드에서 시작해 다음 분기로 넘어가기 전에 해당 분기를 완벽하게 탐색하는 방식
  • 정점의 자식들을 먼저 탐색하는 방식

1. DFS 방식 이해를 위한 예제

  • 탐색 순서: A → B → D → E → F → C → G → H → I → J
  • 루트 노드에서 시작해 다음 분기로 넘어가기 전에 해당 분기를 완벽하게 탐색하는 방식

 

 

 

2. 파이썬으로 그래프를 표현하는 방법

  • 딕셔너리와 리스트 자료구조를 활용하여 그래프를 표현할 수 있음
graph = dict()

graph['A'] = ['B', 'C']
graph['B'] = ['A', 'D']
graph['C'] = ['A', 'G', 'H', 'I']
graph['D'] = ['B', 'E', 'F']
graph['E'] = ['D']
graph['F'] = ['D']
graph['G'] = ['C']
graph['H'] = ['C']
graph['I'] = ['C', 'J']
graph['J'] = ['I']

graph
''' output
{'A': ['B', 'C'],
 'B': ['A', 'D'],
 'C': ['A', 'G', 'H', 'I'],
 'D': ['B', 'E', 'F'],
 'E': ['D'],
 'F': ['D'],
 'G': ['C'],
 'H': ['C'],
 'I': ['C', 'J'],
 'J': ['I']}
'''

 

3. DFS 알고리즘 구현

  • 자료구조 스택과 큐를 활용
    • 큐와 스택 구현은 별도 라이브러리를 활용할 수 있지만, 간단히 파이썬 리스트를 활용할 수 있음
    • need_visit 스택과 visited 큐, 2개의 자료 구조를 생성
  • DFS는 스택과 큐를 활용하고, BFS는 2개의 큐를 활용함
  • 아래 코드는 오른쪽 방향으로 탐색하는 방식으로 구현함
    • 오른쪽이든 왼쪽이든 방향은 정하기 나름이고, 탐색 방식만 같다면 DFS라고 볼 수 있음
def dfs(graph, start_node):
    visited, need_visit = list(), list()
    need_visit.append(start_node)

    while need_visit:
        node = need_visit.pop()
        if node not in visited:
            visited.append(node)
            need_visit.extend(graph[node])

    return visited

dfs(graph, 'A')
# ['A', 'C', 'I', 'J', 'H', 'G', 'B', 'D', 'F', 'E']

 

4. 시간 복잡도

  • 일반적인 DFS 시간 복잡도
    • 노드 수 : V
    • 간선 : E
    • 위 코드에서 while need_visit는 V + E번 수행
    • 시간 복잡도 : O(V + E)
def dfs(graph, start_node):
    visited, need_visit = list(), list()
    need_visit.append(start_node)
    count = 0

    while need_visit:
        count += 1
        node = need_visit.pop()
        if node not in visited:
            visited.append(node)
            need_visit.extend(graph[node])

    print(count)
    return visited

dfs(graph, 'A')
# 19
# ['A', 'C', 'I', 'J', 'H', 'G', 'B', 'D', 'F', 'E']

 

🪵너비우선탐색(BFS, Breadth-First Search)🪵

출처:http://developer-mac.tistory.com/64

  • 정점들과 같은 레벨에 있는 노드들(형제 노드들)을 먼저 탐색하는 방식

1. BFS 방식 이해를 위한 예제

 

  • 탐색 순서 : A → B → C → D → G → H → I → E → F → J
  • 루트노드에서 시작해서 인접한 노드를 먼저 탐색하는 방법으로, 시작정점으로부터 가까운 정점을 먼저 방문하고 멀리 떨어져 있는 정점을 나중에 방문하는 순회 방법
  • 주로 두 노드 사이의 최단 경로를 찾고 싶을 때 이 방법을 선택

 

 

 

2. 파이썬으로 그래프를 표현하는 방법

  • 딕셔너리와 리스트 자료구조를 활용하여 그래프를 표현할 수 있음
graph = dict()

graph['A'] = ['B', 'C']
graph['B'] = ['A', 'D']
graph['C'] = ['A', 'G', 'H', 'I']
graph['D'] = ['B', 'E', 'F']
graph['E'] = ['D']
graph['F'] = ['D']
graph['G'] = ['C']
graph['H'] = ['C']
graph['I'] = ['C', 'J']
graph['J'] = ['I']

graph
''' output
{'A': ['B', 'C'],
 'B': ['A', 'D'],
 'C': ['A', 'G', 'H', 'I'],
 'D': ['B', 'E', 'F'],
 'E': ['D'],
 'F': ['D'],
 'G': ['C'],
 'H': ['C'],
 'I': ['C', 'J'],
 'J': ['I']}
'''

 

3. BFS 알고리즘 구현

  • 자료구조 큐를 활용
    • need_visit 큐와 visited 큐, 2개의 큐를 생성
    • 큐의 구현은 간단히 파이썬 리스트를 활용
data = [1, 2, 3]
data.extend([4, 5])
data

# [1, 2, 3, 4, 5]
def bfs(graph, start_node):
    visited = list()
    need_visit = list()

    need_visit.append(start_node)

    while need_visit:
        node = need_visit.pop(0)
        if node not in visited:
            visited.append(node)
            need_visit.extend(graph[node])

    return visited

bfs(graph, 'A')
# ['A', 'B', 'C', 'D', 'G', 'H', 'I', 'E', 'F', 'J']

 

4. 시간 복잡도

  • 일반적인 BFS 시간 복잡도
    • 노드 수 : V
    • 간선 : E
    • 위 코드에서 while need_visit는 V + E번 수행
    • 시간 복잡도 : O(V + E)
def bfs(graph, start_node):
    visited = list()
    need_visit = list()

    need_visit.append(start_node)
    count = 0

    while need_visit:
        count += 1
        node = need_visit.pop(0)
        if node not in visited:
            visited.append(node)
            need_visit.extend(graph[node])
    print(count)

    return visited

bfs(graph, 'A')
# 19
# ['A', 'B', 'C', 'D', 'G', 'H', 'I', 'E', 'F', 'J']

 

🧩 깊이우선탐색(BFS) vs 너비우선탐색(DFS) 비교

https://namu.wiki/w/너비%20우선%20탐색

 

DFS(깊이우선탐색) BFS(너비우선탐색)
현재 정점에서 갈 수 있는 점들까지 들어가면서 탐색 현재 정점에 연결된 가까운 점들부터 탐색
스택 또는 재귀함수로 구현 큐를 이용해서 구현

 

✓시간복잡도

두 방식 모두 조건 내의 모든 노드를 검색한다는 점에서 시간 복잡도는 동일하다.

DFS와 BFS 둘 다 다음 노드가 방문하였는지를 확인하는 시간과 각 노드를 방문하는 시간을 합하면 된다.