πŸ“ 문제 μ„€λͺ…

  1. μž…λ ₯: 첫째 쀄에 곡백을 두고 μ •μˆ˜ N, M, Vλ₯Ό ν•œ 쀄에 μž…λ ₯ λ°›λŠ”λ‹€.
    • N: μ •μ μ˜ 개수
    • M: κ°„μ„ μ˜ 개수
    • V: 탐색을 μ‹œμž‘ν•  μ •μ μ˜ 번호 (1 - N)
  2. μΈ¨λ ₯: 첫째 쀄에 DFSλ₯Ό μˆ˜ν–‰ν•œ κ²°κ³Όλ₯Ό, κ·Έ λ‹€μŒ μ€„μ—λŠ” BFSλ₯Ό μˆ˜ν–‰ν•œ κ²°κ³Όλ₯Ό 좜λ ₯ν•œλ‹€.
    • VλΆ€ν„° 방문된 점을 μˆœμ„œλŒ€λ‘œ 좜λ ₯
    • λ°©λ¬Έν•  수 μžˆλŠ” 정점이 μ—¬λŸ¬κ°œμΈ 경우 정점 λ²ˆν˜Έκ°€ μž‘μ€κ²ƒμ„ λ¨Όμ € λ°©λ¬Έ
    • μ € 이상 λ°©λ¬Έν•  수 μžˆλŠ” 정점이 μ—†λŠ” 경우 μ’…λ£Œ

문제 링크


πŸ’‘ 아이디어

  1. 첫 μ€„μ—μ„œ 정점 수 N, κ°„μ„  수 M, μ‹œμž‘ 정점 Vλ₯Ό StringTokenizer둜 ν•œλ²ˆμ— νŒŒμ‹±ν•œλ‹€.
  2. κ·Έλž˜ν”„λŠ” 인접 리슀트 λ°©μ‹μœΌλ‘œ ν‘œν˜„ν•œλ‹€.
    • 각 μ •μ λ§ˆλ‹€ μ—°κ²°λœ 정점을 μ €μž₯ν•˜κΈ° μœ„ν•΄ ArrayList<Integer>[] 배열을 μ‚¬μš©
  3. μ–‘λ°©ν–₯ κ°„μ„  μž…λ ₯을 μœ„ν•΄ adj[a].add(b)와 adj[b].add(a)λ₯Ό μ‚¬μš©ν•œλ‹€.
  4. β€˜μ •μ  λ²ˆν˜Έκ°€ μž‘μ€ 것 λΆ€ν„° λ°©λ¬Έν•œλ‹€β€™λΌλŠ” κ·œμΉ™μ„ μœ„ν•΄μ„œ 인접 리슀트(adj[i])λ₯Ό Collections.sort()둜 μ˜€λ¦„μ°¨μˆœμœΌλ‘œ μ •λ ¬ν•œλ‹€.
  5. DFS, BFS 탐색 좜λ ₯μ—μ„œ, λ°©λ¬Έ 배열을 맀번 μ΄ˆκΈ°ν™” ν•΄μ€€λ‹€. (visited = new boolean[N + 1];) κ·Έλ ‡μ§€ μ•ŠμœΌλ©΄ 탐색 κ²°κ³Όκ°€ μ˜€μ—Ό 될 수 μžˆλ‹€.
  6. DFS λ‘œμ§μ€ μž¬κ·€λ‘œ κ΅¬ν˜„ν•˜κ³ , BFS λ‘œμ§μ€ 큐둜 κ΅¬ν˜„ν•œλ‹€.
    • DFSλŠ” v λ°©λ¬Έ 정점 자체λ₯Ό λ§€κ°œλ³€μˆ˜λ‘œ λ°›λŠ”λ‹€.
    • BFS의 λ§€κ°œλ³€μˆ˜λŠ” start둜, μ‹œμž‘μ λ§Œ μ •μ˜ν•΄μ€„ 뿐이닀.
    • 이후 인접 정점은 queue.offer()둜 큐에 μΆ”κ°€ν•œλ‹€.
    • κ·ΈλŸ¬λ―€λ‘œ .poll ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•΄μ„œ 이미 λ°©λ¬Έν•œ 정점은 μ‚­μ œν•˜μ—¬ 정점 λ°©λ¬Έ μˆœμ„œλ₯Ό μ œμ–΄ν•œλ‹€.

πŸ”Ž μ˜ˆμ‹œ κ·Έλž˜ν”„ μ‹œκ°ν™”

κ·Έλž˜ν”„


πŸ›  μ½”λ“œ

✨ 제좜 μ½”λ“œ

import java.io.*;
import java.util.*;

public class Main {
    // 정점 수, κ°„μ„  수, μ‹œμž‘ 정점 번호
    static int N, M, V;

    // 인접 리슀트 (μ •μ λ§ˆλ‹€ μ—°κ²°λœ 정점 λͺ©λ‘)
    static List<Integer>[] adj;

    // λ°©λ¬Έ μ—¬λΆ€ μ €μž₯ λ°°μ—΄
    static boolean[] visited;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;
        
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken()); 
        V = Integer.parseInt(st.nextToken());
        
        // κ·Έλž˜ν”„ μ΄ˆκΈ°ν™” (1~N)
        adj = new ArrayList[N + 1];
        for (int i = 1; i <= N; i++) {
            adj[i] = new ArrayList<>();
        }
        
        // κ°„μ„  정보 μž…λ ₯
        for (int i = 0; i < M; i++) {
            st = new StringTokenizer(br.readLine());
            int a = Integer.parseInt(st.nextToken());
            int b = Integer.parseInt(st.nextToken());
            // μ–‘λ°©ν–₯ μž…λ ₯
            adj[a].add(b);
            adj[b].add(a);
        }
        
        // μž‘μ€ 정점을 λ¨Όμ € λ°©λ¬Έν•˜κΈ° μœ„ν•œ μ •λ ¬
        for (int i = 1; i <= N; i++) {
            Collections.sort(adj[i]);
        }
        
        // DFS 좜λ ₯
        visited = new boolean[N + 1];
        dfs(V);
        System.out.println();
        
        // BFS 좜λ ₯
        visited = new boolean[N + 1];
        bfs(V);
        System.out.println();
    }
    
    // DFS - μž¬κ·€λ‘œ κ΅¬ν˜„
    static void dfs(int v) {
        visited[v] = true;
        System.out.print(v + " ");
        for (int nxt : adj[v]) {
            if (!visited[nxt]) {
                dfs(nxt);
            }
        }
    }
    
    // BFS - 큐둜 κ΅¬ν˜„
    static void bfs(int start) {
        Queue<Integer> queue = new LinkedList<>();
        visited[start] = true;
        queue.offer(start);
        
        while (!queue.isEmpty()) {
            int v = queue.poll();
            System.out.print(v + " ");
            for (int nxt : adj[v]) {
                if (!visited[nxt]) {
                    visited[nxt] = true;
                    queue.offer(nxt);
                }
            }
        }
    }
}

πŸͺ„ 디버깅 μ½”λ“œ

import java.io.*;
import java.util.*;

public class Main {
    // 정점 수, κ°„μ„  수, μ‹œμž‘ 정점 번호
    static int N, M, V;

    // 인접 리슀트 (μ •μ λ§ˆλ‹€ μ—°κ²°λœ 정점 λͺ©λ‘)
    static List<Integer>[] adj;

    // λ°©λ¬Έ μ—¬λΆ€ μ €μž₯ λ°°μ—΄
    static boolean[] visited;

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

        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        V = Integer.parseInt(st.nextToken());
        System.out.println("N=" + N + ", M=" + M + ", V=" + V);

        // κ·Έλž˜ν”„ μ΄ˆκΈ°ν™” (1~N)
        adj = new ArrayList[N + 1];
        for (int i = 1; i <= N; i++) {
            adj[i] = new ArrayList<>();
        }

        // κ°„μ„  정보 μž…λ ₯
        for (int i = 0; i < M; i++) {
            st = new StringTokenizer(br.readLine());
            int a = Integer.parseInt(st.nextToken());
            int b = Integer.parseInt(st.nextToken());

            adj[a].add(b); // a β†’ b μ—°κ²°
            adj[b].add(a); // b β†’ a μ—°κ²°
            System.out.println("κ°„μ„  μΆ”κ°€: " + a + " ↔ " + b);
        }

        // μž‘μ€ 정점을 λ¨Όμ € λ°©λ¬Έν•˜κΈ° μœ„ν•œ μ •λ ¬
        for (int i = 1; i <= N; i++) {
            Collections.sort(adj[i]);
        }

        // 전체 κ·Έλž˜ν”„ ꡬ쑰 확인 좜λ ₯
        System.out.println("μ •λ ¬λœ 인접 리슀트:");
        for (int i = 1; i <= N; i++) {
            System.out.println("  " + i + " β†’ " + adj[i]);
        }
        System.out.println();

        // DFS 좜λ ₯
        // DFS와 BFSλŠ” μ„œλ‘œ 영ν–₯을 μ£Όμ§€ μ•Šλ„λ‘ visited 각각 μ΄ˆκΈ°ν™”
        visited = new boolean[N + 1];
        System.out.println("===== DFS μ‹œμž‘ =====");
        dfs(V);
        System.out.println("\n===== DFS μ’…λ£Œ =====\n");

        // BFS 좜λ ₯
        visited = new boolean[N + 1]; // λ°©λ¬Έ λ°°μ—΄ λ‹€μ‹œ μ΄ˆκΈ°ν™”
        System.out.println("===== BFS μ‹œμž‘ =====");
        bfs(V);
        System.out.println("\n===== BFS μ’…λ£Œ =====");
    }

    // ===== DFS ν•¨μˆ˜ μ •μ˜ =====
    // 깊이 μš°μ„  탐색: κ°€λŠ₯ν•œ ν•œ 깊게 λ“€μ–΄κ°€λŠ” 방식 (μž¬κ·€λ‘œ κ΅¬ν˜„)
    static void dfs(int v) {
        visited[v] = true; // ν˜„μž¬ 정점 λ°©λ¬Έ 처리
        System.out.println("[DFS] λ°©λ¬Έ β†’ " + v);

        // ν˜„μž¬ 정점에 μ—°κ²°λœ 인접 정점듀을 μˆœμ„œλŒ€λ‘œ 확인
        for (int nxt : adj[v]) {
            System.out.println("  [DFS] " + v + "의 이웃 체크 β†’ " + nxt);

            // 아직 λ°©λ¬Έν•˜μ§€ μ•Šμ•˜λ‹€λ©΄ μž¬κ·€ 호좜둜 계속 듀어감
            if (!visited[nxt]) {
                System.out.println("    [DFS] β†’ " + nxt + " μž¬κ·€ 호좜");
                dfs(nxt);
            } else {
                System.out.println("    [DFS] 이미 방문함 β†’ " + nxt);
            }
        }
    }

    // ===== BFS ν•¨μˆ˜ μ •μ˜ =====
    // λ„ˆλΉ„ μš°μ„  탐색: κ°€κΉŒμš΄ λ…Έλ“œλΆ€ν„° νƒμƒ‰ν•˜λŠ” 방식 (큐둜 κ΅¬ν˜„)
    static void bfs(int start) {
        Queue<Integer> queue = new LinkedList<>(); // 탐색에 μ‚¬μš©ν•  큐
        visited[start] = true;                     // μ‹œμž‘ 정점 λ°©λ¬Έ 처리
        queue.offer(start);                        // 큐에 μ‹œμž‘ 정점 λ„£κΈ°
        System.out.println("[BFS] μ‹œμž‘ 정점 β†’ " + start);

        // 큐가 빌 λ•ŒκΉŒμ§€ 반볡
        while (!queue.isEmpty()) {
            // νμ—μ„œ κΊΌλ‚΄λ©΄μ„œ ν˜„μž¬ 탐색할 정점 선택
            int v = queue.poll();
            System.out.println("[BFS] poll()둜 νμ—μ„œ 제거 β†’ " + v);

            // ν˜„μž¬ 정점과 μ—°κ²°λœ λͺ¨λ“  이웃 정점 확인
            for (int nxt : adj[v]) {
                System.out.println("  [BFS] " + v + "의 이웃 체크 β†’ " + nxt);

                // 아직 λ°©λ¬Έν•˜μ§€ μ•Šμ€ 정점이면 큐에 μΆ”κ°€
                if (!visited[nxt]) {
                    visited[nxt] = true;
                    queue.offer(nxt);
                    System.out.println("    [BFS] 큐에 μΆ”κ°€ β†’ " + nxt);
                } else {
                    System.out.println("    [BFS] 이미 방문함 β†’ " + nxt);
                }
            }
        }
    }
}

πŸͺ„ 디버깅 μ½”λ“œ μ‹€ν–‰ κ²°κ³Ό

5 5 3
N=5, M=5, V=3
5 4
κ°„μ„  μΆ”κ°€: 5 ↔ 4
5 2
κ°„μ„  μΆ”κ°€: 5 ↔ 2
1 2
κ°„μ„  μΆ”κ°€: 1 ↔ 2
3 4
κ°„μ„  μΆ”κ°€: 3 ↔ 4
3 1
κ°„μ„  μΆ”κ°€: 3 ↔ 1
μ •λ ¬λœ 인접 리슀트:
  1 β†’ [2, 3]
  2 β†’ [1, 5]
  3 β†’ [1, 4]
  4 β†’ [3, 5]
  5 β†’ [2, 4]

===== DFS μ‹œμž‘ =====
[DFS] λ°©λ¬Έ β†’ 3
  [DFS] 3의 이웃 체크 β†’ 1
    [DFS] β†’ 1 μž¬κ·€ 호좜
[DFS] λ°©λ¬Έ β†’ 1
  [DFS] 1의 이웃 체크 β†’ 2
    [DFS] β†’ 2 μž¬κ·€ 호좜
[DFS] λ°©λ¬Έ β†’ 2
  [DFS] 2의 이웃 체크 β†’ 1
    [DFS] 이미 방문함 β†’ 1
  [DFS] 2의 이웃 체크 β†’ 5
    [DFS] β†’ 5 μž¬κ·€ 호좜
[DFS] λ°©λ¬Έ β†’ 5
  [DFS] 5의 이웃 체크 β†’ 2
    [DFS] 이미 방문함 β†’ 2
  [DFS] 5의 이웃 체크 β†’ 4
    [DFS] β†’ 4 μž¬κ·€ 호좜
[DFS] λ°©λ¬Έ β†’ 4
  [DFS] 4의 이웃 체크 β†’ 3
    [DFS] 이미 방문함 β†’ 3
  [DFS] 4의 이웃 체크 β†’ 5
    [DFS] 이미 방문함 β†’ 5
  [DFS] 1의 이웃 체크 β†’ 3
    [DFS] 이미 방문함 β†’ 3
  [DFS] 3의 이웃 체크 β†’ 4
    [DFS] 이미 방문함 β†’ 4

===== DFS μ’…λ£Œ =====

===== BFS μ‹œμž‘ =====
[BFS] μ‹œμž‘ 정점 β†’ 3
[BFS] poll()둜 νμ—μ„œ 제거 β†’ 3
  [BFS] 3의 이웃 체크 β†’ 1
    [BFS] 큐에 μΆ”κ°€ β†’ 1
  [BFS] 3의 이웃 체크 β†’ 4
    [BFS] 큐에 μΆ”κ°€ β†’ 4
[BFS] poll()둜 νμ—μ„œ 제거 β†’ 1
  [BFS] 1의 이웃 체크 β†’ 2
    [BFS] 큐에 μΆ”κ°€ β†’ 2
  [BFS] 1의 이웃 체크 β†’ 3
    [BFS] 이미 방문함 β†’ 3
[BFS] poll()둜 νμ—μ„œ 제거 β†’ 4
  [BFS] 4의 이웃 체크 β†’ 3
    [BFS] 이미 방문함 β†’ 3
  [BFS] 4의 이웃 체크 β†’ 5
    [BFS] 큐에 μΆ”κ°€ β†’ 5
[BFS] poll()둜 νμ—μ„œ 제거 β†’ 2
  [BFS] 2의 이웃 체크 β†’ 1
    [BFS] 이미 방문함 β†’ 1
  [BFS] 2의 이웃 체크 β†’ 5
    [BFS] 이미 방문함 β†’ 5
[BFS] poll()둜 νμ—μ„œ 제거 β†’ 5
  [BFS] 5의 이웃 체크 β†’ 2
    [BFS] 이미 방문함 β†’ 2
  [BFS] 5의 이웃 체크 β†’ 4
    [BFS] 이미 방문함 β†’ 4

===== BFS μ’…λ£Œ =====

⏱ μ‹œκ°„ λ³΅μž‘λ„

  • μž…λ ₯ 처리:
    M개의 간선을 읽고 인접 리슀트(adj)에 μΆ”κ°€ β†’ O(M)

  • μ •λ ¬:
    각 정점에 λŒ€ν•΄ μ •λ ¬ Collections.sort() μˆ˜ν–‰ β†’ O(N * log D) (μ—¬κΈ°μ„œ DλŠ” 각 μ •μ μ˜ 평균 인접 정점 수, μ΅œμ•…μ˜ 경우 D = N) β†’ μ΅œμ•…: O(N log N) (μ •μ λ§ˆλ‹€ μ—°κ²°λœ 정점이 λ§Žμ„ 경우)

  • DFS (깊이 μš°μ„  탐색): 각 정점을 ν•œλ²ˆμ”© λ°©λ¬Έν•˜λ©° 인접 정점 탐색 β†’ O(N + M)

  • BFS (λ„ˆλΉ„ μš°μ„  탐색): β€˜νλ₯Ό μ΄μš©ν•΄μ„œβ€™ 각 정점을 ν•œλ²ˆμ”© λ°©λ¬Έν•˜λ©° 인접 정점 탐색 β†’ O(N + M)

  • 좜λ ₯:
    System.out.print()을 ν†΅ν•œ 순차 좜λ ₯ β†’ O(N) (μ΅œλŒ€ N개 정점 λ°©λ¬Έ)

πŸ‘‰ 총 μ‹œκ°„ λ³΅μž‘λ„:
O(M + N log N)


✏️ λŠλ‚€ 점

  • 디버깅 μ½”λ“œλ‘œ μ²˜λ¦¬κ³Όμ •μ„ λͺ¨λ“  단계 ν†Ίμ•„λ³΄λŠ”κ²Œ 이해에 λ§Žμ€ 도움이 됐닀.
  • BFSλŠ” μ΅œλ‹¨ 경둜만 μ‚¬μš©ν•˜λ―€λ‘œ μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” 간선이 μžˆλ‹€.