티스토리 뷰
[Java+Python]24444번, 알고리즘 수업 - 너비 우선 탐색 1
Vagabund.Gni 2023. 8. 3. 16:27목차
문제
오늘도 서준이는 너비 우선 탐색(BFS) 수업 조교를 하고 있다.
아빠가 수업한 내용을 학생들이 잘 이해했는지 문제를 통해서 확인해 보자.
N개의 정점과 M개의 간선으로 구성된 무방향 그래프(undirected graph)가 주어진다.
정점 번호는 1번부터 N번이고 모든 간선의 가중치는 1이다.
정점 R에서 시작하여 너비 우선 탐색으로 노드를 방문할 경우 노드의 방문 순서를 출력하자.
너비 우선 탐색 의사 코드는 다음과 같다.
인접 정점은 오름차순으로 방문한다.
bfs(V, E, R) { # V : 정점 집합, E : 간선 집합, R : 시작 정점
for each v ∈ V - {R}
visited[v] <- NO;
visited[R] <- YES; # 시작 정점 R을 방문 했다고 표시한다.
enqueue(Q, R); # 큐 맨 뒤에 시작 정점 R을 추가한다.
while (Q ≠ ∅) {
u <- dequeue(Q); # 큐 맨 앞쪽의 요소를 삭제한다.
for each v ∈ E(u) # E(u) : 정점 u의 인접 정점 집합.(정점 번호를 오름차순으로 방문한다)
if (visited[v] = NO) then {
visited[v] <- YES; # 정점 v를 방문 했다고 표시한다.
enqueue(Q, v); # 큐 맨 뒤에 정점 v를 추가한다.
}
}
}
입력
첫째 줄에 정점의 수 N (5 ≤ N ≤ 100,000), 간선의 수 M (1 ≤ M ≤ 200,000), 시작 정점 R (1 ≤ R ≤ N)이 주어진다.
다음 M개 줄에 간선 정보 u v가 주어지며 정점 u와 정점 v의 가중치 1인 양방향 간선을 나타낸다. (1 ≤ u < v ≤ N, u ≠ v)
모든 간선의 (u, v) 쌍의 값은 서로 다르다.
출력
첫째 줄부터 N개의 줄에 정수를 한 개씩 출력한다.
i번째 줄에는 정점 i의 방문 순서를 출력한다.
시작 정점의 방문 순서는 1이다.
시작 정점에서 방문할 수 없는 경우 0을 출력한다.
풀이
지난번까지의 DFS에 이어 이번엔 BFS이다. 논리적으로는 그게 그거일 것 같지만,
BFS는 DFS에 비해 조금 덜 직관적이고, 문제풀이에 다른 자료구조를 요구한다.
그 자료구조라 함은 바로 큐(Queue)인데, 자료구조 공부할 때 배운 대로 큐가 아닌 ArrayDeque을 생성해 큐처럼 사용했다.
그 이유는 아래 글에 나온다.
[면접 준비 - Java]Data Structures in a Nutshell
어쨌거나 이번 풀이도 문제 자체를 이해하는 게 어렵지는 않으니, 풀이를 보며 하나씩 받아들여보자.
public class Prob24444 {
static List<Integer>[] graph;
static boolean[] visited;
static int[] order;
static int count = 1;
먼저 DFS 문제를 풀 때와 마찬가지로 그래프, 방문여부, 순서, 현재까지 방문한 수를 위해 전역변수를 선언한다.
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken());
int m = Integer.parseInt(st.nextToken());
int r = Integer.parseInt(st.nextToken());
graph = new ArrayList[n + 1];
for (int i = 0; i <= n; i++) {
graph[i] = new ArrayList<>();
}
visited = new boolean[n + 1];
order = new int[n + 1];
입력으로 주어진 n, m, r을 할당하고, n을 기준으로 graph, visited, order를 초기화해 준다.
for (int i = 0; i < m; i++) {
st = new StringTokenizer(br.readLine());
int u = Integer.parseInt(st.nextToken());
int v = Integer.parseInt(st.nextToken());
graph[u].add(v);
graph[v].add(u);
}
for (int i = 1; i <= n; i++) {
Collections.sort(graph[i]);
}
이어서 m을 기준으로 그래프를 채워 넣고, 각 요소를 문제의 조건에 맞게 오름차순으로 정렬한다.
bfs(r);
이후에 너비 우선 탐색을 위한 bfs가 호출되는데, 이는 아래와 같이 구현되어 있다.
static void bfs(int node) {
ArrayDeque<Integer> queue = new ArrayDeque<>();
visited[node] = true;
order[node] = count++;
queue.offer(node);
입력으로 출발 노드를 받고, 위에 적은 방법대로 큐를 생성한 뒤,
해당 인덱스의 방문여부를 true로 바꾸고 해당 인덱스의 방문 순서를 1 더해 할당한다.
이어서 해당 노드를 큐에 집어넣고 다음 로직으로 이어진다.
while (!queue.isEmpty()) {
int current = queue.poll();
for (int next : graph[current]) {
if (!visited[next]) {
visited[next] = true;
order[next] = count++;
queue.offer(next);
}
}
}
보다시피 큐가 비어있지 않은 동안 아래의 로직이 계속 실행된다.
- 큐의 첫 번째 요소(현재 요소)를 가져와서 해당 인덱스의 그래프를 순회한다.
- 인접 노드 중 반문하지 않는 것이 있다면 해당 노드를 방문하고, 방문여부와 순서를 갱신한다.
- 방문한 노드를 큐에 추가한다.
그래프는 이미 오름차순으로 정렬되어 있기 때문에, 위와 같이 반복하면 그래프의 모든 노드를 BFS로 방문하게 된다.
BFS를 한 마디로 하자면 '가까운 노드부터 차례로 방문'이기 때문에 위와 같은 로직은 적절하다.
for (int i = 1; i <= n; i++) {
System.out.println(order[i]);
}
마지막으로 방문 순서가 들어있는 order 배열을 순서대로 출력하면 끝이다.
Java
package BackJoon;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.StringTokenizer;
public class Prob24444 {
static List<Integer>[] graph;
static boolean[] visited;
static int[] order;
static int count = 1;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken());
int m = Integer.parseInt(st.nextToken());
int r = Integer.parseInt(st.nextToken());
graph = new ArrayList[n + 1];
for (int i = 0; i <= n; i++) {
graph[i] = new ArrayList<>();
}
visited = new boolean[n + 1];
order = new int[n + 1];
for (int i = 0; i < m; i++) {
st = new StringTokenizer(br.readLine());
int u = Integer.parseInt(st.nextToken());
int v = Integer.parseInt(st.nextToken());
graph[u].add(v);
graph[v].add(u);
}
for (int i = 1; i <= n; i++) {
Collections.sort(graph[i]);
}
bfs(r);
for (int i = 1; i <= n; i++) {
System.out.println(order[i]);
}
}
static void bfs(int node) {
ArrayDeque<Integer> queue = new ArrayDeque<>();
visited[node] = true;
order[node] = count++;
queue.offer(node);
while (!queue.isEmpty()) {
int current = queue.poll();
for (int next : graph[current]) {
if (!visited[next]) {
visited[next] = true;
order[next] = count++;
queue.offer(next);
}
}
}
}
}
Python
from collections import deque
import sys
n, m, r = map(int, sys.stdin.readline().rstrip().split(" "))
graph = [[] for _ in range(n + 1)]
visited = [False] * (n + 1)
order = [0] * (n + 1)
cnt = 1
def bfs(node):
global cnt
queue = deque()
visited[node] = True
order[node] = cnt
cnt += 1
queue.append(node)
while queue:
current = queue.popleft()
for next in sorted(graph[current]):
if not visited[next]:
visited[next] = True
order[next] = cnt
cnt += 1
queue.append(next)
for _ in range(m):
u, v = map(int, sys.stdin.readline().rstrip().split(" "))
graph[u].append(v)
graph[v].append(u)
bfs(r)
for i in range(1, n + 1):
print(order[i])
파이썬의 경우 자체적으로 제공하는 deque을 이용해서 풀었다.
이외의 과정은 순서만 조금 다를 뿐 거의 같다.
Performance
'Algorithm > [Java+Python+JavaScript]BackJoon' 카테고리의 다른 글
[Java+Python]1260번, DFS와 BFS (0) | 2023.08.19 |
---|---|
[Java+Python]2606번, 바이러스 - DFS/BFS (0) | 2023.08.18 |
[Java+Python]24445번, 알고리즘 수업 - 너비 우선 탐색 2 (0) | 2023.08.05 |
[JavaScript]2745번, 진법 변환 (0) | 2023.08.02 |
[Java+Python]24480번, 알고리즘 수업 - 깊이 우선 탐색 2 (0) | 2023.07.30 |
[JavaScript]2566번, 최댓값, 이차원 배열, 두 번째 입력방식 (0) | 2023.07.25 |
- Total
- Today
- Yesterday
- 리스트
- 여행
- java
- 동적계획법
- Algorithm
- 세계일주
- Backjoon
- spring
- RX100M5
- 지지
- 기술면접
- 유럽여행
- 칼이사
- Python
- 세계여행
- 알고리즘
- 파이썬
- 세모
- 백준
- 면접 준비
- 스트림
- 유럽
- 맛집
- 자바
- 중남미
- 야경
- BOJ
- a6000
- 스프링
- 남미
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |