안녕하세요
인포돈 입니다.
백준 알고리즘 1260번 풀이입니다.
* 참고사항
- 개발환경은 eclipse을 기준으로 작성되었습니다.
- java언어를 이용하여 문제를 풀이합니다.
- 알고리즘 문제는 풀이를 보고 해답을 찾는 것도 중요하지만 무엇보다 스스로 풀이를 시도해봐야 합니다!!
(해당 풀이를 보기 전 충분히 문제에 대해 고민해봐야 합니다!)
- 해당 풀이 말고도 수많은 풀이 방법이 존재합니다.
백준 1260 (DFS와 BFS)
문제
그래프를 DFS로 탐색한 결과와 BFS로 탐색한 결과를 출력하는 프로그램을 작성하시오. 단, 방문할 수 있는 정점이 여러 개인 경우에는 정점 번호가 작은 것을 먼저 방문하고, 더 이상 방문할 수 있는 점이 없는 경우 종료한다. 정점 번호는 1번부터 N번까지이다.
입력
첫째 줄에 정점의 개수 N(1 ≤ N ≤ 1,000), 간선의 개수 M(1 ≤ M ≤ 10,000), 탐색을 시작할 정점의 번호 V가 주어진다. 다음 M개의 줄에는 간선이 연결하는 두 정점의 번호가 주어진다. 어떤 두 정점 사이에 여러 개의 간선이 있을 수 있다. 입력으로 주어지는 간선은 양방향이다
출력
첫째 줄에 DFS를 수행한 결과를, 그다음 줄에는 BFS를 수행한 결과를 출력한다. V부터 방문된 점을 순서대로 출력하면 된다.
입력, 출력 예제
성공 코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;
public class Main {
static StringBuilder sb = new StringBuilder();
static boolean[] check;
static int[][] arr;
static int node, line, start;
static Queue<Integer> q = new LinkedList<>();
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
node = Integer.parseInt(st.nextToken());
line = Integer.parseInt(st.nextToken());
start= Integer.parseInt(st.nextToken());
arr = new int[node+1][node+1];
check = new boolean[node+1];
for(int i = 0 ; i < line ; i ++) {
StringTokenizer str = new StringTokenizer(br.readLine());
int a = Integer.parseInt(str.nextToken());
int b = Integer.parseInt(str.nextToken());
arr[a][b] = arr[b][a] = 1;
}
//sb.append("\n");
dfs(start);
sb.append("\n");
check = new boolean[node+1];
bfs(start);
System.out.println(sb);
}
public static void dfs(int start) {
check[start] = true;
sb.append(start + " ");
for(int i = 0 ; i <= node ; i++) {
if(arr[start][i] == 1 && !check[i])
dfs(i);
}
}
public static void bfs(int start) {
q.add(start);
check[start] = true;
while(!q.isEmpty()) {
start = q.poll();
sb.append(start + " ");
for(int i = 1 ; i <= node ; i++) {
if(arr[start][i] == 1 && !check[i]) {
q.add(i);
check[i] = true;
}
}
}
}
}
DFS와 BFS 정말 유명한 알고리즘이지만 저는 처음 접해보았습니다. 뭐 기본적으로 DFS와 BFS가 무슨 의미를 갖는지는 알고 있었죠. 이번에 알고리즘으로 다시 풀어보면서 느꼈지만 DFS는 스택, 재귀 BFS는 큐를 활용하여 구현한다는 것을 알 수 있었습니다. 정확한 구현 법은 STEP을 따라가며 이해해보도록 하죠
알고리즘 흐름도
STEP1 문제의 이해
문제의 이해는 아주 간단하다 DFS 즉 깊이 우선으로 탐색하는 방법과 BFS로 탐색한 결과를 출력하면 되는 문제이다.
여기서 DFS란 깊이 우선 탑색으로 예제 1번으로 들어보자
1 2
1 3
1 4
2 4
3 4
가 들어왔는데 처음으로 1 2가 들어오면 우리는 그다음으로 2 4 그다음 이어지는 곳이 없으니 다시 1 3 3 4 또 이어지는 곳이 없으니 1 4 이런 식으로 탐색이 되는 것입니다.
BFS라면 넓이 우선 탐색이니 당연히
1 2
1 3
1 4
2 4
3 4
이런 식으로 탐색이 될 것입니다.
STEP2 입력받기
StringTokenizer st = new StringTokenizer(br.readLine());
node = Integer.parseInt(st.nextToken());
line = Integer.parseInt(st.nextToken());
start= Integer.parseInt(st.nextToken());
arr = new int[node+1][node+1];
check = new boolean[node+1];
for(int i = 0 ; i < line ; i ++) {
StringTokenizer str = new StringTokenizer(br.readLine());
int a = Integer.parseInt(str.nextToken());
int b = Integer.parseInt(str.nextToken());
arr[a][b] = arr[b][a] = 1;
}
입력받기란 뭐... 어려울 게 없습니다. 단 arr이 무엇인지 궁금해하시는 분들이 있으실 겁니다. 바로 노드 간의 간선을 표현해 주기 위한 표이죠.
1 | 2 | 3 | 4 | |
1 | 1 | 1 | 1 | |
2 | 1 | 1 | ||
3 | 1 | 1 | ||
4 | 1 | 1 | 1 |
이런 식으로 말이죠 (입력 예제 1번 활용) 이러한 방법을 인접 행렬 방법이라고도 하네요
따른 방법은 인접 리스트를 활용한 방법이 있기도 합니다.
1번 노드 ->
3 | 2 | 4 |
2번 노드 ->
4 |
3번 노드 ->
4 |
4번 노드 ->
2 | 3 |
그런다 해당 문제에서는 숫자가 낮은 노드부터 탐색하려고 하니, 해당 방법은 또 따른 정렬을 통해 탐색해야 될 듯싶네요
STEP 3 DFS 구현하기
public static void dfs(int start) {
check[start] = true;
sb.append(start + " ");
for(int i = 0 ; i <= node ; i++) {
if(arr[start][i] == 1 && !check[i])
dfs(i);
}
}
DFS구현은 재귀를 활용하는 방법을 사용하였습니다. (스택도 있지만, 보다 간편한 방법으로 구현해보았습니다.)
이번 DFS를 보면 시작점을 줍니다. 그 시작 접을 CHECK BOOLEAN배열을 통해 체크해 줍니다.
재귀를 통해서 만약 arr이 ==1이라면 간선이 있다는 표시겠죠? 그다음 check가 false라면 탐색하지 않았던 노드를 의미하고요.
또한 for문은 0부터 시작하기에 해당 노드에 거리게 되면 dfs(i)로 더 깊이 즉 DFS가 바로 실행이 먼저 되게 됩니다.
FOR문이 끝마치기 전에 DFS를 먼저 끝낸다.!
STEP 4 BFS 구현하기
사실 저는 BFS를 구현하기가 가장 까다로웠습니다. 흠.. 큐를 잘못 생각해버려서죠..
public static void bfs(int start) {
q.add(start);
check[start] = true;
while(!q.isEmpty()) {
start = q.poll();
sb.append(start + " ");
for(int i = 1 ; i <= node ; i++) {
if(arr[start][i] == 1 && !check[i]) {
q.add(i);
check[i] = true;
}
}
}
}
큐 또한 시작 노드를 받아줍니다. 큐에는 해당 노드를 넣어준 뒤 탐색을 시작합니다.
큐의 처음 삽입한 노드를 빼주고 그 노드와 연결되어있는 (ARR 배열) 노드들을 탐색하여 Q에 넣습니다. 이와 같은 경우 DFS와 는 다르게 처음 노드와 연결된 넓이 노드부터 Q에 들어가는 것을 알 수 있죠.
이러한 큐를 반복해서 꺼내 주다가 큐가 비어지면 끝!!
(사실 큐의 FIFO와 덱 큐의 LIFO를 순간 착각해서.... 오래 걸렸던 문제였던 것 같습니다...)
사실 이리 보면 DFS BFS는 단지 무엇을 먼저 찾을지가 관건인 거 같긴 한다. 다른 DFS와 BFS문제를 풀어보니 좀 더 각 기법에 맡는 문제들이 있어 보입니다.
이러한 문제들을 더 풀어보면서 DFS와 BFS에 대한 감을 익혀보도록 하겠습니다.
'알고리즘 > 백준알고리즘' 카테고리의 다른 글
[백준알고리즘-JAVA]2667번 풀이(단지번호붙이기) - 초보도 이해하는 풀이 (0) | 2021.10.06 |
---|---|
[백준알고리즘-JAVA]2606번 풀이(바이러스) - 초보도 이해하는 풀이 (6) | 2021.09.28 |
[백준알고리즘-JAVA]1874번 풀이(스택 수열) - 초보도 이해하는 풀이 (2) | 2021.09.24 |
[백준알고리즘-JAVA]4949번 풀이(균형잡힌 세상) - 초보도 이해하는 풀이 (0) | 2021.09.21 |
[백준알고리즘-JAVA]9012번 풀이(괄호) - 초보도 이해하는 풀이 (0) | 2021.09.20 |