alorithm/Baekjoon

[JAVA/백준] 24479번 : 알고리즘 수업 - 깊이 우선 탐색 1

Hannana. 2023. 8. 18. 01:00
728x90
반응형

[문제]
오늘도 서준이는 깊이 우선 탐색(DFS) 수업 조교를 하고 있다. 아빠가 수업한 내용을 학생들이 잘 이해했는지 문제를 통해서 확인해보자.

N개의 정점과 M개의 간선으로 구성된 무방향 그래프(undirected graph)가 주어진다. 정점 번호는 1번부터 N번이고 모든 간선의 가중치는 1이다. 정점 R에서 시작하여 깊이 우선 탐색으로 노드를 방문할 경우 노드의 방문 순서를 출력하자.

깊이 우선 탐색 의사 코드는 다음과 같다. 인접 정점은 오름차순으로 방문한다.

dfs(V, E, R) {  # V : 정점 집합, E : 간선 집합, R : 시작 정점
    visited[R] <- YES;  # 시작 정점 R을 방문 했다고 표시한다.
    for each x ∈ E(R)  # E(R) : 정점 R의 인접 정점 집합.(정점 번호를 오름차순으로 방문한다)
        if (visited[x] = NO) then dfs(V, E, x);
}



<Input>
첫째 줄에 정점의 수 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) 쌍의 값은 서로 다르다.


<Output>

첫째 줄부터 N개의 줄에 정수를 한 개씩 출력한다. i번째 줄에는 정점 i의 방문 순서를 출력한다. 시작 정점의 방문 순서는 1이다. 시작 정점에서 방문할 수 없는 경우 0을 출력한다.


<examples>
-입력

5 5 1
1 4
1 2
2 3
2 4
3 4


-출력

1
2
3
4
0

정점 1번에서 정점 2번을 방문한다. 정점 2번에서 정점 3번을 방문한다. 정점 3번에서 정점 4번을 방문한다. 정점 5번은 정점 1번에서 방문할 수 없다.

 



[풀이]

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.StringTokenizer;

@SuppressWarnings("unchecked")
public class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer st;
    static StringBuilder sb = new StringBuilder();
    static ArrayList<ArrayList<Integer>> A = new ArrayList<>();//정점들의 정보를 기록할 그래프
    static int[] visited;
    static int count; //순회 순서

    public static void main(String[] args) throws IOException {
         st = new StringTokenizer(br.readLine());

        int N = Integer.parseInt(st.nextToken());
        int M = Integer.parseInt(st.nextToken());
        int R = Integer.parseInt(st.nextToken());

        visited = new int[N+1];
        for (int i=0;i<N+1;i++){
            A.add(new ArrayList<>());
        }
        for (int i=0;i<M;i++){
            st = new StringTokenizer(br.readLine());
            int s = Integer.parseInt(st.nextToken());
            int e = Integer.parseInt(st.nextToken());
            A.get(s).add(e);
            A.get(e).add(s); //양방향이므로 양쪽에 더하기
        }
        //오름차순 정렬
        for (int i=1;i<A.size();i++){
            Collections.sort(A.get(i)); 
        }
        count = 1; 
        DFS(R); //DFS 재귀 시작
        for (int i=1;i<visited.length;i++){
            sb.append(visited[i]).append("\n");
        }
        System.out.println(sb);
    }
    private static void DFS(int R) {
        visited[R] = count;

        for(int i=0;i<A.get(R).size();i++){ //현재 위치한 정점이 갈 수 있는 정점 리스트를 순회
            int newV = A.get(R).get(i);
            if(visited[newV] == 0){
                count++; 
                DFS(newV); 
            }
        }
    }
}

 


 

DFS 의 개념과 코드를 접목시키기란 쉽지 않은 일인데

일반적으로 정점vertex를 순회하며 그 정점을 출력하는 것까지는 오케이였으나

갑자기 순서...를 출력하라하니 머리가 정지가 됐었다..

일단 main 메소드에서 입력값을 받는 것까지는 동일함.

추가로 무방향그래프에 방문 순서 정보를 저장해야하므로

단순히 방문여부를 저장하는 visit[] 배열 외에 정수형태의 count 변수를 지정하여

정점마다 해당 정보를 저장하게끔 했다. 

그래프는 연결리스트 안에 연결리스트가 있는 형태로 구현했다.

양쪽으로 정점을 연결하는 것 역시 무방향그래프의 특징!

 

그래프 구현을 마친 뒤,

문제에 나온대로 각 정점에 연결 된 정점들을 오름차 순으로 순회할 수 있도록

각 정점을 돌며 오름차 순 정렬해주었다. (Collections.sort() 사용)

 

이제,

DFS 메소드를 구현해야 하는데

DFS 자료구조는 깊이 우선 탐색으로, 스택 혹은 재귀 함수 형태로 구현이 가능하다.

 

 

(다음 글 참고)

 

[JAVA/알고리즘] 깊이우선탐색 (DFS) 이란? / 자바 사용 예시 총 정리

탐색 : 많은 양의 데이터 중에서 원하는 데이터를 찾는 과정 어떤 메모리 구조를 가졌느냐에 따라 효율적인 탐색 방법이 갈리긴 하지만, 일반적으로 탐색에 쓰이는 알고리즘 중 대표적인 DFS에

hansjour.tistory.com

 

물론! 재귀함수도 자료 구조의 형태로 바라보면 스택 메모리를 활용한다는 점에서

둘은 크게 다르지 않지만(실제 성능 또한 큰 차이 없다.)

이번엔 가독성이 좋은 재귀함수를 사용.

 

 

문제에서 주어진 수도 코드를 참고하여 함수를 구현하였다.

DFS 함수로 시작 노드를 호출하여 재귀를 시작,

해당 정점에 연결 된 정점들(아까 오름차 순 정렬해둔)을 for문으로 순회하며

각 노드들의 순회 여부를 판단,

미순회 노드로 판단 시, 순회 순서를 위해 지정해 둔 count 변수를 할당해주고

DFS 함수를 재귀 호출하여 해당 분기가 끝날 때까지

스택 형태로 무한 반복한다.

만약, 해당 분기의 모든 정점을 순회했다면 시작 정점을 바꾸어 다시 재귀 형태로 순회 시작.

 

DFS의 개념은 알고 있었지만 코드로 구현하려니 어려움이 있었는데,

다양한 강의와 자료들을 참고하여 한줄 씩 이해하고 나니

DFS 구현에 대한 이해도가 생긴 느낌이다.

 

아무튼 

....이번 문제도 완료.

 

 

 

 

 

 

 

반응형