본문 바로가기

알고리즘

BFS 너비 우선 탐색

너비 우선 탐색이란?

시작점에서 가까운 정점부터 순서대로 방문하는 탐색 알고리즘

 

방법?

  1. 각 정점을 방문할 때마다 모든 인접 정점들을 검사
  2. 이 때 처음 보는 정점을 방문하면 이 정점을 방문 예정이라고 기록해 둔 뒤, 별도의 위치에 저장
  3. 인접한 정점을 모두 검사하고 나면, 지금까지 저장한 목록에서 다음 정점을 꺼내서 방문

즉, BFS의 방문 순서는정점의 목록에서 어떤 정점을 먼저 꺼내는지에 의해 결정

 

수행 예

 

C++ 코드

#include <iostream>
#include <vector>
#include <queue>
using namespace std;

vector<vector<int>> adj;

// 너비 우선 탐색 구현
// start에서 시작해 그래프를 너비 우선 탐색하고 각 정점의 방문 순서를 반환
vector<int> bfs(int start) {
	// 각 정점의 방문 여부
	vector<bool> discovered(adj.size(), false);
	// 방문할 정점 목록을 유지하는 큐
	queue<int> q;
	// 정점의 방문순서
	vector<int> order;

	discovered[start] = true;
	q.push(start);
	while (!q.empty()) {
		int here = q.front();
		q.pop();
		//here 방문
		order.push_back(here);
		//모든 인접 정점 검사
		for (int i = 0; i < adj[here].size(); i++) {
			int there = adj[here][i];
			// 처음 보는 정점이면 방문 목록에 집어넣음
			if (!discovered[there]) {
				q.push(there);
				discovered[there] = true;
			}
		}
	}
	return order;
}

 

너비 우선 탐색에서는, 모든 정점은 아래와 같은 3개의 상태를 순서대로 거쳐감

1. 아직 발견되지 않은 상태

2. 발견되었지만 아직 방문되지 않은 상태 (큐에 저장)

3. 방문된 상태

 

새 정점을 발견하는 데 사용했던 간선들만을 모은 트리를 너비 우선 탐색 스패닝 트리라고 부름

 

시간 복잡도?

모든 정점을 한번씩 방문 + 정점을 방문할 때마다 인접한 모든 간선 검사

인접 리스트로 구현된 경우에는 O(|V|+|E|)

인접 행렬로 구현된 경우에는 O(|V|^2)

 

너비 우선 탐색과 최단 거리

너비 우선 탐색이 대개 사용되는 용도는 그래프에서의 최단 경로 문제

즉, 두 정점을 연결하는 경로 중 길이가 가장 짧은 경로를 찾는 문제

 

 

예제

백준 2178 미로탐색

https://www.acmicpc.net/problem/2178

 

2178번: 미로 탐색

첫째 줄에 두 정수 N, M(2 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 M개의 정수로 미로가 주어진다. 각각의 수들은 붙어서 입력으로 주어진다.

www.acmicpc.net

 

문제

N×M크기의 배열로 표현되는 미로가 있다.

1 0 1 1 1 1
1 0 1 0 1 0
1 0 1 0 1 1
1 1 1 0 1 1

미로에서 1은 이동할 수 있는 칸을 나타내고, 0은 이동할 수 없는 칸을 나타낸다. 이러한 미로가 주어졌을 때, (1, 1)에서 출발하여 (N, M)의 위치로 이동할 때 지나야 하는 최소의 칸 수를 구하는 프로그램을 작성하시오. 한 칸에서 다른 칸으로 이동할 때, 서로 인접한 칸으로만 이동할 수 있다.

위의 예에서는 15칸을 지나야 (N, M)의 위치로 이동할 수 있다. 칸을 셀 때에는 시작 위치와 도착 위치도 포함한다.

 

 

입력

첫째 줄에 두 정수 N, M(2 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 M개의 정수로 미로가 주어진다. 각각의 수들은 붙어서 입력으로 주어진다.

 

출력

첫째 줄에 지나야 하는 최소의 칸 수를 출력한다. 항상 도착위치로 이동할 수 있는 경우만 입력으로 주어진다.

 

 

풀이

→ BFS 이용

처음 위치 (0, 0)에서부터 시작해서, map의 크기를 초과하지 않는 범위 내에서 상하좌우 위치 방문하면서

map의 좌표값이 1일 경우 해당 좌표값을 (현재 위치 + 1)로 변경하고, 큐에 해당 좌표 인덱스 추가

 

코드

using namespace std;
#include <iostream>
#include <queue>

int N, M;
int map[101][101];
int dx[4] = { -1, 0, 0, 1 };
int dy[4] = { 0, -1, 1, 0 };
queue<pair<int, int>> q;

void bfs() {
	q.push(make_pair(0, 0));

	while (!q.empty()) {
		int y = q.front().first;
		int x = q.front().second;
		q.pop();

		for (int i = 0; i < 4; i++) {
			int ny = y + dy[i], nx = x + dx[i];
			if (nx < 0 || ny < 0 || nx >= M || ny >= N) continue;
			if (map[ny][nx] == 1) {
				map[ny][nx] = map[y][x] + 1;
				if (ny == N - 1 && nx == M - 1) cout << map[ny][nx];
				q.push(make_pair(ny, nx));
			}
		}
	}
}

int main() {
	string s;
	cin >> N >> M;
	for (int i = 0; i < N; i++) {
		cin >> s;
		for (int j = 0; j < M; j++) {
			map[i][j] = s[j]-'0';
		}
	}
	bfs();

	return 0;
}

 

 

 

참고 : 알고리즘 문제해결 전략

 

'알고리즘' 카테고리의 다른 글

펜윅 트리(fenwick tree)  (0) 2022.02.21
탐욕법(Greedy)  (0) 2022.02.07
우선순위 큐  (0) 2021.09.07