새소식

algorithm/이론

깊이 우선 탐색(DFS)와 너비 우선 탐색(BFS)

  • -

DFS/BFS

DFS와 BFS는 대표적인 그래프 탐색 알고리즘이다.

DFS는 스택 자료구조 또는 재귀 함수를 통해 구현할 수 있고, BFS는 큐 자료구조를 통해 알고리즘을 구현할 수 있다.

그래프는 노드간선으로 표현되며 이때 노드를 정점이라고도 한다.
그래프 탐색이란 하나의 노드를 시작으로 다수의 노드를 방문하는 것을 말한다.
또한 두 노드가 간선으로 연결되어 있다면 ‘두 노드는 인접하다’라고 표현한다.
프로그래밍에서 그래프는 크게 2가지 방식으로 표현할 수 있다.

  • 인접 행렬 : 2차원 배열로 그래프의 연결 관계를 표현하는 방식
  • 인접 리스트 : 리스트로 그래프의 연결 관계를 표현하는 방식

메모리 측면에서 인접 행렬 방식은 모든 관계를 저장하므로 노드의 개수가 많을 수록 메모리가 불필요하게 낭비된다.
반면 인접 리스트 방식은 연결된 정보만을 저장하기 때문에 메모리를 효율적으로 사용할 수 있다.
속도 측면에서 보자면 인접 리스트 방식은 인접 행렬 방식에 비해 특정한 두 노드가 연결되어 있는지에 대한 정보를 얻는 속도가 느리다. 인접 리스트 방식에서는 연결된 데이터를 하나씩 확인해야 하기 때문이다.

DFS

DFS깊이 우선 탐색 알고리즘으로 그래프에서 깊은 부분을 우선적으로 탐색하는 알고리즘이다.

DFS는 특정한 경로를 탐색하다가 특정한 상황에서 최대한 깊숙이 들어가서 노드를 방문한 후, 다시 돌아가 다른 경로로 탐색하는 알고리즘이다.
DFS는 탐색을 수행함에 있어 데이터 개수가 N개인 경우 O(N)의 시간이 소요된다는 특징이 있다.

DFS의 구체적인 동작 과정은 다음과 같다.

  1. 탐색 시작 노드를 스택에 삽입하고 방문 처리를 한다.
  2. 스택의 최상단 노드에 방문하지 않은 인접 노드가 있으면, 그 인접 노드를 스택에 넣고 방문 처리를 한다. 방문하지 않은 인접 노드가 없으면 스택에서 최상단 노드를 꺼낸다.
  3. 2번 과정을 더 이상 수행할 수 없을 때까지 반복한다.

DFS

DFS는 스택 자료구조 또는 재귀 함수를 이용하여 구현할 수 있다.
아래는 Java로 DFS를 구현한 예시이다.

public class ExampleDFS {
    public static void main(String[] args) {
        // 0 = A
        dfs(0);
        System.out.println(sb);
    }

    private static void dfs(int current) {
      // 현재 노드를 방문 처리한다.
        visited[current] = true;
        char node = (char) (current + 65);
        writeCurrentNode(node);

        // 현재 노드와 인접한 노드를 찾는다.
        for (int i = 0; i < size; i++) {
          // 인접한 노드를 방문하지 않았다면, 해당 노드를 방문한다.
            if (arr[current][i] == 1 && !visited[i]) {
                dfs(i);
            }
        }
    }

    private static void writeCurrentNode(char node) {
        sb.append(node);
        hasNext();
    }

    private static int[][] arr = {
            {0, 1, 1, 1, 0, 0, 0, 0, 0, 0},
            {1, 0, 0, 0, 1, 1, 0, 0, 0, 0},
            {1, 0, 0, 0, 0, 0, 1, 1, 0, 0},
            {1, 0, 0, 0, 0, 0, 0, 0, 1, 0},
            {0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 1, 0, 0, 0, 0, 0, 0, 1},
            {0, 0, 1, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 1, 0, 0, 0}
    };
    private static int size = 10;
    private static boolean[] visited = new boolean[size];
    private static StringBuffer sb = new StringBuffer();

    private static void hasNext() {
        boolean result = true;
        for (int i = 0; i < size; i++) {
            result = result && visited[i];
        }
        if (!result) {
            sb.append(" > ");
        }
    }
}

위 코드의 결과는 다음과 같다.

# 결과. 좌측에서 우측으로 방문 순서를 나타낸다.
A > B > E > F > C > G > J > H > D > I

BFS

BFS너비 우선 탐색 알고리즘으로 가까운 노드부터 탐색하는 알고리즘이다.

BFS는 인접한 노드를 먼저 탐색하는 방법으로, 시작 노드로부터 가까운 노드를 먼저 방문하고 멀리 떨어져 있는 노드를 나중에 방문하는 순회 방법이다.
주로 두 노드 사이의 최단 경로를 찾고 싶을 때 이 방법을 선택한다.

BFS 구현에서는 선입선출 방식인 큐 자료구조를 이용한다.
인접한 노드를 반복적으로 큐에 넣도록 알고리즘을 작성하면 자연스럽게 먼저 들어온 것이 먼저 나가게 되어, 가까운 노드부터 탐색을 진행하게 된다.

BFS의 구체적인 동작 과정은 다음과 같다.

  1. 탐색 시작 노드를 큐에 삽입하고 방문 처리를 한다.
  2. 큐에서 노드를 꺼내 해당 노드의 인접 노드 중에서 방문하지 않은 노드를 모두 큐에 삽입하고 방문 처리를 한다.
  3. 2번의 과정을 더 이상 수행할 수 없을 때까지 반복한다.

BFS

아래는 Java로 BFS를 구현한 예시이다.

import java.util.LinkedList;
import java.util.Queue;

public class ExampleBFS {

    public static void main(String[] args) {
        // 0 = A
        bfs(0);
        System.out.println(sb);
    }

    private static void bfs(int start) {
        // 현재 노드를 큐에 넣고 방문 처리한다.
        visit(start);

        while (!queue.isEmpty()) {
            Integer current = queue.poll();
            char node = (char) (current + 65);
            writeCurrentNode(node);

            // 현재 노드와 인접한 노드를 찾는다.
            for (int i = 0; i < size; i++) {
                // 인접한 노드를 방문하지 않았다면, 해당 노드를 방문한다.
                if (arr[current][i] == 1 && !visited[i]) {
                    visit(i);
                }
            }
        }
    }

    private static void visit(int i) {
        queue.add(i);
        visited[i] = true;
    }

    private static void writeCurrentNode(char node) {
        sb.append(node);
        hasNext();
    }

    private static void hasNext() {
        boolean result = true;
        for (int i = 0; i < size; i++) {
            result = result && visited[i];
        }
        if (!queue.isEmpty() || !result) {
            sb.append(" > ");
        }
    }

    private static int[][] arr = {
            {0, 1, 1, 1, 0, 0, 0, 0, 0, 0},
            {1, 0, 0, 0, 1, 1, 0, 0, 0, 0},
            {1, 0, 0, 0, 0, 0, 1, 1, 0, 0},
            {1, 0, 0, 0, 0, 0, 0, 0, 1, 0},
            {0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 1, 0, 0, 0, 0, 0, 0, 1},
            {0, 0, 1, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 1, 0, 0, 0}
    };
    private static int size = 10;
    private static boolean[] visited = new boolean[size];
    private static StringBuffer sb = new StringBuffer();
    private static Queue<Integer> queue = new LinkedList<>();
}

위 코드의 결과는 다음과 같다.

# 결과. 좌측에서 우측으로 방문 순서를 나타낸다.
A > B > C > D > E > F > G > H > I > J

'algorithm > 이론' 카테고리의 다른 글

[Algorithm] 비트마스킹(Bitmasking) (Java)  (0) 2022.12.20
이진 탐색 알고리즘  (0) 2022.12.01
정렬 알고리즘  (0) 2022.12.01
최단 경로 알고리즘  (0) 2022.11.26
깊이 우선 탐색(DFS)와 너비 우선 탐색(BFS)

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.