[알고리즘 / C++] BFS 알고리즘 (너비 우선 탐색)

BFS 알고리즘 C++

BFS 알고리즘 이란?

DFS 설명 보려면 이전 글을 참고하자

BFS(Breadth-First Search) 는 DFS와 더불어 그래프에서 많이 사용하는 알고리즘이다.

체감상 DFS보다 BFS를 사용하는 문제를 더 많이 접하게 되는 것 같다.

DFS와 마찬가지로 BFS 알고리즘은 한 노드에서 출발해 연결된 모든 노드들을 방문하게 된다.

하지만 DFS가 가능한 한 깊게 탐색하고 되돌아 오는 것에 반해 BFS는 가까운 모든 노드들을 먼저 방문한 후 깊이 들어가게 된다.

BFS 알고리즘 작동 방식

BFS 알고리즘이 어떻게 작동하는지 살펴보자

BFS는 큐(Queue) 자료구조를 사용해서 구현한다

  1. 큐 초기화
  2. 시작 노드 방문
  3. 인접 노드 큐에 추가
  4. 큐가 빌때까지 반복
BFS 알고리즘 C++

DFS를 설명했던 노드 그림을 그대로 가져와서 설명해 보도록 하겠다.

  1. 큐 초기화

시작 노드는 0으로 설정하였다.

Queue : [0]

BFS 알고리즘 C++

Queue : [1, 2]

2. 시작 노드 방문

3. 인접노드 큐에 추가

큐에서 시작노드 0을 뺀 후 0과 연결되어 있는 모든 노드들을 큐에 추가한다.

여기서는 1, 2가 연결되어 있기 때문에 queue에는 0, 1이 들어가게 된다.

BFS 알고리즘 C++

4. 큐에 없어질때까지 반복

노드 1 방문

Queue : [2, 3]

큐는 FIFO로 먼저들어간 1이 먼저 나오게 된다.

1을 방문한 후 1과 연결된 노드들을 모두 추가해 준다.

여기서는 3이 있기 때문에 큐에 3을 넣어 준다.

BFS 알고리즘 C++

노드 2 방문

Queue : [3, 4, 5]

큐의 제일 앞에 있는 2를 빼서 방문한다.

2와 연결되어 있는 노드들을 모두 큐에 넣는다.

여기서는 4, 5가 연결되어 있기 때문에 큐는 원래 있던 3과 4, 5가 추가된다.

BFS 알고리즘 C++

노드 3 방문

Queue : [4, 5]

큐의 제일 앞에 있는 3을 꺼내어 방문한다.

3에는 더이상 연결되어 있는 노드가 없기 때문에 아무것도 추가하지 않는다.

BFS 알고리즘 C++

노드 4 방문

Queue : [5]

큐의 제일 앞에 있는 4를 꺼내어 방문한다.

4도 더이상 연결되어 있는 노드가 없기 때문에 추가하지 않는다.

BFS 알고리즘 C++

노드 5 방문

Queue : []

큐의 제일 앞에 있는 5를 꺼내어 방문한다.

더이상 추가할 노드가 없고, 큐도 비었기 때문에 BFS를 종료하게 된다.

결론

BFS 알고리즘 ( 너비 우선 탐색)은 주변 노드들을 먼저 탐색하기 때문에

0 -> 1 -> 2 -> 3 -> 4 -> 5의 순서대로 탐색하게 된다.

어떤 문제에서 BFS를 써야하는가

BFS는 활용도가 무궁무진하다고 생각한다.

그리고 그 중에 많이 쓰이는 것들을 몇개 추려 보았다

  • 최단 경로 찾기 (노드 간, 2차원)
  • 바이러스 문제
  • 미로찾기

대부분의 문제 유형이 비슷한 느낌인데

그래프 문제에서 주로 최단경로, 적절한 길을 찾는 문제에 많이 사용한다고 생각하면 될 것 같다.

최단 경로에서 BFS가 유용한 이유는 BFS 알고리즘이 현재 노드의 인접 노드들을 하나씩 모두 확인하기 때문에 가장 먼저 찾아지는 경로가 곧 최단거리이기 때문이다!!

BFS 알고리즘 장단점 / 시간복잡도

장점

  • 최단경로를 보장한다
  • 알고리즘이 꽤 간단하다

단점

  • 메모리 소모가 크다
  • 비효율적이다.

최단경로를 찾을 수는 있지만 그래프의 크기가 매우 클 경우 메모리 소모가 크고 최악의 비효율적일 수 있다. 따라서 그래프의 크기가 크다면 dfs를 사용하는 것이 더 맞다.

시간복잡도

노드 수 (V), 간선 수 (E) 일때

O(V+E)의 시간복잡도를 가진다.

C++ BFS 알고리즘 구현

BFS 알고리즘을 #include <queue> 헤더를 사용해서 구현하면 간단하게 할 수 있다.

DFS와 마찬가지로 visited 배열이 있어야 하고

각 노드에 방문할 때마다 visited 배열을 true로 만들고 연결된 노드들을 큐에 넣어 준다.

그리고 큐가 빌때까지(모두 방문할때까지) while문을 돌리며 반복한다.

void BFS(int V, vector<int> adj[], int s) {
    vector<bool> visited(V, false);
    queue<int> queue;

    visited[s] = true;
    queue.push(s);

    while(!queue.empty()) {
        s = queue.front();
        cout << s << " ";
        queue.pop();

        for (int i = 0; i < adj[s].size(); i++) {
            if (!visited[adj[s][i]]) {
                visited[adj[s][i]] = true;
                queue.push(adj[s][i]);
            }
        }
    }
}

전체적인 방법은 DFS와 비슷한 것 같다.

단지 queue냐 stack이냐의 차이로 구현하면 될 것 같다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다