ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [알고리즘][개념] DFS(깊이 우선 탐색) & BFS(너비 우선 탐색)
    개발 이야기/알고리즘 및 코테 준비 2024. 9. 18. 15:28
    https://devuna.tistory.com/32, 튜나 개발일기를 참고하였습니다. 

     

    여러 자료구조 중 그래프를 탐색하는 방법에는 크게 깊이 우선 탐색, DFS와 너비 우선 탐색인 BFS가 존재한다. 

    여기서 말하는 그래프란 node와 edge로 구성된 자료구조를 말한다. 이 중 방향성이 있는 비순환 그래프를 트리라고 말한다. 

    이런 그래프를 탐색한다는 의미는 하나의 정점으로부터 시작하여 차례대로 모든 정점을 하나씩 방문하는 것을 의미한다. 

     

    코테 문제 중 일부는 이런 search 기법을 잘 구현해야하기에 DFS와 BFS의 개념을 알아보고 python으로 주어진 그래프를 간단히 탐색해보는 예제코드를 작성해보려고 한다. 

     

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

    : 우선 최대한 깊이 내려간 뒤에, 더 이상 갈 수 없다면 옆으로 가자!

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

    깊이 우선 탐색이란 위의 이미지에서 볼 수 있듯이, 루트 노드(혹은 임의의 노드)에서 시작해서 다음 분기로 넘어가기 전에 해당 분기를 완벽하게(깊숙히 끝까지) 탐색하는 방법이다. 

    스택 또는 재귀함수로 구현한다.

     

    아래의 특징이 있다. 

    1. 모든 노드를 방문하고자 하는 경우에 이 방법을 선택함
    2. 깊이 우선 탐색(DFS)이 너비 우선 탐색(BFS)보다 좀 더 간단함
    3. 검색 속도 자체는 너비 우선 탐색(BFS)에 비해서 느림

     

    너비 우선 탐색(BFS, Breadth-First Search): 1층 1층 보자..!(layer 단으로 보자)

    출처 https://developer-mac.tistory.com/64 루

    루트 노드(혹은 다른 임의의 노드)에서 시작해석 인접한 노드를 먼저 탐색하는 방법으로, 시작 정점으로부터 가까운 정점들, 즉 이웃 노드들을 우선 방문하는 방법이다. 큐를 이용해서 구현한다. 

     

    주로 두 노드 사이의 최단 경로를 찾고 싶을 때 이 방법을 선택한다. 

    DFS와 BFS 비교 

    DFS, BFS은 특징에 따라 사용에 더 적합한 문제 유형들이 있습니다.

    1) 그래프의 모든 정점을 방문하는 것이 주요한 문제

    단순히 모든 정점을 방문하는 것이 중요한 문제의 경우 DFS, BFS 두 가지 방법 중 어느 것을 사용하셔도 상관없습니다.

    둘 중 편한 것을 사용하시면 됩니다.


    2) 경로의 특징을 저장해둬야 하는 문제

    예를 들면 각 정점에 숫자가 적혀있고 a부터 b까지 가는 경로를 구하는데 경로에 같은 숫자가 있으면 안 된다는 문제 등, 각각의 경로마다 특징을 저장해둬야 할 때는 DFS를 사용합니다. (BFS는 경로의 특징을 가지지 못합니다)

    3) 최단거리 구해야 하는 문제

    미로 찾기 등 최단거리를 구해야 할 경우, BFS가 유리합니다.
    왜냐하면 깊이 우선 탐색으로 경로를 검색할 경우 처음으로 발견되는 해답이 최단거리가 아닐 수 있지만, 
    너비 우선 탐색으로 현재 노드에서 가까운 곳부터 찾기 때문에 경로를 탐색 시 먼저 찾아지는 해답이 곧 최단거리기 때문입니다.


    이밖에도 
    - 검색 대상 그래프가 정말 크다면 DFS를 고려
    - 검색대상의 규모가 크지 않고, 검색 시작 지점으로부터 원하는 대상이 별로 멀지 않다면 BFS

    간단한 코드 구현(python)

    트리 구현

    class TreeNode:
        def __init__(self, value):
            self.value = value
            self.children = []
    
        def add_child(self, node):
            self.children.append(node)
            
    # 트리 생성
    root = TreeNode(1)
    child_1 = TreeNode(2)
    child_2 = TreeNode(3)
    child_3 = TreeNode(4)
    
    root.add_child(child_1)
    root.add_child(child_2)
    root.add_child(child_3)
    
    child_1.add_child(TreeNode(5))
    child_3.add_child(TreeNode(6))
    child_3.add_child(TreeNode(7))

     

    구현된 트리는 아래와 같습니다.

           1
         / | \
        2  3  4
       /      / \
      5      6   7

    DFS 알고리즘: 

    1. 재귀함수를 이용한 구현

    def dfs(node):
        if node is None:
            return
        print(node.value, end=" ")  # 현재 노드 출력
        for child in node.children:
            dfs(child)  # 자식 노드에 대해 재귀 호출

    2. 스택을 이용한 구현

    스택의 경우 LIFO를 따름으로 이를 이용. 아래의 방식으로 작동

    1. 루트 노드를 스택에 추가합니다.
    2. 스택에서 노드를 꺼내고 그 노드를 방문합니다.
    3. 꺼낸 노드의 자식들을 스택에 추가하는데, 자식을 오른쪽에서 왼쪽 순서로 넣어야 스택에서 꺼낼 때 왼쪽 자식부터 방문하게 됩니다.
    4. 스택이 비어있을 때까지 위 과정을 반복합니다.
    def dfs_stack(root):
        if root is None:
            return
        
        stack = [root]  # 스택에 루트 노드를 넣음
        
        while stack:
            node = stack.pop()  # 스택에서 가장 위의 노드를 꺼냄
            print(node.value, end=" ")  # 현재 노드 출력
            
            # 자식 노드를 스택에 추가 (반대로 추가해야 왼쪽부터 탐색됨)
            # 자식들을 오른쪽에서 왼쪽 순으로 스택에 넣어야 왼쪽부터 탐색됨
            for child in reversed(node.children):
                stack.append(child)

     

    1 -> 2 -> 5 -> 3 -> 4 -> 6 -> 7 순서로 탐색함 

    BFS 알고리즘: 

    큐를 이용하여 구현. 큐의 경우 FIFO 임으로 탐색할 노드를 큐에 넣고, 이후 그 다음 자식 노드들을 큐에 차례대로 넣어가며 탐색한다. 

    from collections import deque
    
    def bfs(root):
        if root is None:
            return
        queue = deque([root])  # 큐에 루트 노드를 삽입
        while queue:
            node = queue.popleft()  # 큐의 앞에서 노드를 꺼내고 출력
            print(node.value, end=" ")
            queue.extend(node.children)  # 현재 노드의 자식들을 큐에 삽입

     

    BFS는 트리의 레벨 순서대로 탐색합니다. 먼저 루트 노드 1을 방문하고, 그 다음 레벨에서 2, 3, 4를 방문한 후, 그다음 레벨에서 5, 6, 7을 방문합니다. BFS 탐색 순서는 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7입니다

Designed by Tistory.