문제 정보
1260번: DFS와 BFS
첫째 줄에 정점의 개수 N(1 ≤ N ≤ 1,000), 간선의 개수 M(1 ≤ M ≤ 10,000), 탐색을 시작할 정점의 번호 V가 주어진다. 다음 M개의 줄에는 간선이 연결하는 두 정점의 번호가 주어진다. 어떤 두 정점 사
www.acmicpc.net
핵심
DFS와 BFS에 대해서 알고 있는지?
그래프를 탐색하는 알고리즘에 대한 지식을 가지고 있는지?
그들을 어떤 Data Structure를 통해 구현해야 하는지 알고 있는지?
개인적으로 내부 알고리즘에서 중요한 요소는 이렇게 된다고 생각한다
- 해당 노드를 방문했는지 확인하는 boolean visitied[]
- 노드들 사이 간선을 판단하는 boolean edge[][]
- BFS의 경우 FIFO를 가능케 하는 Queue<>
DFS는 대부분 Stack을 통하여 구현한다
1. 시작 노드를 Stack에 삽입한다
2. Stack에서 한 요소를 pop하고 출력한다. 이 때, 해당 노드를 방문한 것으로 표시한다
3. pop한 노드와 간선으로 연결된 다른 노드들 중, 방문하지 않은 노드를 Stack에 삽입한다
4. 2~3의 과정을 반복하고, 모든 노드가 방문되었다면 프로그램을 종료한다
BFS는 대부분 Queue를 통하여 구현한다
1. 시작 노드를 Queue에 삽입한다
2. Queue에서 한 요소를 pop하고 출력한다. 이 때, 해당 노드를 방문한 것으로 표시한다
3. pop한 노드와 간선으로 연결된 다른 노드들 중, 방문하지 않은 노드를 Queue에 삽입한다
4. 2~3의 과정을 반복하고, 모든 노드가 방문되었다면 프로그램을 종료한다
풀이
// 1260: DFS, BFS
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 boolean[][] edge;
static boolean[] visited;
static StringBuilder sb;
static Queue<Integer> queue;
public static void main(String[] args) throws IOException {
int N, M, V;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken()) + 1;
M = Integer.parseInt(st.nextToken());
V = Integer.parseInt(st.nextToken());
edge = new boolean[N][N];
for (int i = 0; i < M; i++) {
st = new StringTokenizer(br.readLine());
int n1 = Integer.parseInt(st.nextToken());
int n2 = Integer.parseInt(st.nextToken());
edge[n1][n2] = true;
edge[n2][n1] = true;
}
visited = new boolean[N];
sb = new StringBuilder();
dfs(V, N, 0);
System.out.println(sb.toString());
visited = new boolean[N];
sb = new StringBuilder();
queue = new LinkedList<>();
bfs(V, N, 0);
System.out.println(sb.toString());
}
public static void dfs(int current, int end, int depth) {
if (depth == end)
return;
visited[current] = true;
sb.append(current + " ");
for (int i = 1; i < end; i++) {
if (edge[current][i] && !visited[i]) {
dfs(i, end, depth + 1);
}
}
}
public static void bfs(int current, int end, int depth) {
if (depth == end)
return;
visited[current] = true;
sb.append(current + " ");
for (int i = 1; i < end; i++) {
if (edge[current][i] && !visited[i]) {
visited[i] = true;
queue.add(i);
}
}
if (!queue.isEmpty())
bfs(queue.remove(), end, depth + 1);
}
}
나는 DFS의 경우 인접 행렬 방식으로 풀이했는데,
그 이유는 문제에서
단, 방문할 수 있는 정점이 여러 개인 경우에는 정점 번호가 작은 것을 먼저 방문하고, 더 이상 방문할 수 있는 점이 없는 경우 종료한다.
해당 조건이 주어져있었기 때문에, 일부러 정렬을 해줄 필요가 없이 for loop 을 통하여 인접한 행렬을
Recursive call을 통하여 번호가 작은 노드부터 차례대로 방문할 수 있게 해 주었다
고찰
중간중간에 depth와 같은 variable은 사용하지 않아도 풀 수 있을 것 같아 불필요해 보였다
또한, main->dfs나 main->bfs로 여러 variable을 넘겨주기 위하여 global variable 처리를 하고 풀이하였는데
이부분을 더 효율적으로 구현할 수 있는 방법이 있을것 같다
아래 코드는 백준 알고리즘 다른사람 풀이를 참고하면서, 내가 구현하고 싶었던 대로 깔끔하게 구현되어 있는 코드를 찾아 가져와 보았다
// 출처 : https://www.acmicpc.net/source/54591054
import java.util.*;
import java.io.*;
class Main {
static int N, M, R;
static boolean[] visited;
static ArrayList<Integer>[] graph;
static ArrayList<Integer> dfsAns = new ArrayList<>();
static ArrayList<Integer> bfsAns = new ArrayList<>();
static Queue<Integer> queue = new LinkedList<>();
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine(), " ");
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
R = Integer.parseInt(st.nextToken());
visited = new boolean[N+1];
graph = new ArrayList[N+1];
for (int i = 1; i <= N; i++) {
graph[i] = new ArrayList<>();
}
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]);
dfs(R);
bfs(R);
StringBuilder sb = new StringBuilder();
for (int i : dfsAns) sb.append(i).append(" ");
sb.append("\n");
for (int i : bfsAns) sb.append(i).append(" ");
System.out.println(sb);
}
static void dfs(int r) {
visited[r] = true;
dfsAns.add(r);
for (int adj : graph[r]) {
if (!visited[adj]) {
dfs(adj);
}
}
}
static void bfs(int r) {
visited = new boolean[N+1];
visited[r] = true;
bfsAns.add(r);
queue.add(r);
while (!queue.isEmpty()) {
int u = queue.poll();
for (int adj : graph[u]) {
if (!visited[adj]) {
visited[adj] = true;
bfsAns.add(adj);
queue.add(adj);
}
}
}
}
}
인접 행렬을 2중 Array를 사용하지 않고, 2중 List를 사용하여 풀이하셨고
이러한 경우 장점은 foreach로 해당 노드와 인접한 노드를 탐방할 수 있어서 연결되어 있는지 굳이 확인할 필요 없어서 더 효율적인 것 같다
하지만 이 경우에 아까 말했던 특정 노드와 연결된 노드가 한 개 이상인 경우, 가장 작은 노드부터 방문해야 하기 때문에
Collections.sort() 를 사용하여 정렬을 꼭 해주어야 하는 단점이 있다!
Source Code
댓글