κ·Έλž˜ν”„(Graph) μžλ£Œκ΅¬μ‘°μ™€ 탐색

κ·Έλž˜ν”„(Graph)

  • λΉ„μ„ ν˜• 자료ꡬ쑰
  • μ›μ†Œλ“€ κ°„ n:m의 관계λ₯Ό κ°€μ§€λŠ” 자료ꡬ쑰
  • 사물 ν˜Ήμ€ μ‚¬λžŒ, 좔상적 κ°œλ… λ“±μ˜ 관계성을 ν‘œν˜„ν•œ 자료ꡬ쑰
  • 정점(vertex)λ“€μ˜ 집합과 이듀을 μ—°κ²°ν•˜λŠ” κ°„μ„ (Edge)λ“€μ˜ μ§‘ν•©μœΌλ‘œ κ΅¬μ„±λœ 자료ꡬ쑰
  • 차수(Degree) : 정점에 μ—°κ²°λœ κ°„μ„ μ˜ 수

κ·Έλž˜ν”„μ˜ μœ ν˜•

  • 무ν–₯ κ·Έλž˜ν”„(Undirected Graph) : 간선에 λ°©ν–₯이 μ—†μ–΄ 정점 κ°„ μ—°κ²°κ΄€κ³„λ§Œ λ‚˜νƒ€λ‚Έ κ·Έλž˜ν”„
  • 유ν–₯ κ·Έλž˜ν”„(Directed Graph) : 간선에 λ°©ν–₯이 μžˆμ–΄ 좜발 정점과 도착 정점을 λ‚˜νƒ€λ‚Έ κ·Έλž˜ν”„
  • κ°€μ€‘μΉ˜ κ·Έλž˜ν”„(Weighted Graph) : 간선에 κ°€μ€‘μΉ˜κ°€ λΆ€μ—¬λ˜μ–΄ νŠΉμ • 정점 κ°„ 거리, 이동 λΉ„μš©, 기타 κ°€μ€‘μΉ˜ λ“±μ˜ 값을 ν‘œν˜„ν•œ κ·Έλž˜ν”„
  • 사이클 μ—†λŠ” λ°©ν–₯ κ·Έλž˜ν”„(DAG, Directed Acyclic Graph) : ν•œ μ •μ μ—μ„œ λ‹€λ₯Έ 정점듀을 λŒμ•„ λ‹€μ‹œ μžμ‹ μ—κ²Œ λŒμ•„μ˜€λŠ” μˆœν™˜μ μΈ 사이클이 μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ”, 일방ν–₯μ„± κ·Έλž˜ν”„
  • νŠΈλ¦¬λ„ κ·Έλž˜ν”„μ˜ 일쒅

    • λΆ€λͺ¨ λ…Έλ“œμ™€ μžμ‹ λ…Έλ“œ μ‚¬μ΄μ—λŠ” μœ μΌν•œ κ²½λ‘œκ°€ μ‘΄μž¬ν•¨

인접 정점과 κ·Έλž˜ν”„ 경둜

  • 인접(Adjacency) : 두 정점 사이에 간선이 μ‘΄μž¬ν•  경우 두 정점은 μ—°κ²°λ˜μ–΄ 있고 μΈμ ‘ν•œ κ²ƒμž„
  • 경둜(Path) : ν•œ μ •μ μ—μ„œ μ‹œμž‘ν•˜μ—¬ λ‹€λ₯Έ μ •μ κΉŒμ§€ λ„λ‹¬ν•˜κΈ°κΉŒμ§€μ˜ κ°„μ„  정보

    • λ‹¨μˆœ 경둜 : μ‹œμž‘ 정점과 끝 정점을 μ œμ™Έν•˜κ³  μ€‘λ³΅λœ 정점이 μ—†μŒ
    • 사이클(Cyclic) : 경둜의 μ‹œμž‘ 정점과 끝 정점이 κ°™μŒ

κ·Έλž˜ν”„μ˜ ν‘œν˜„

인접 ν–‰λ ¬(Adjacent Matrix)

  • V * V 크기의 2차원 배열을 μ΄μš©ν•˜μ—¬ 두 정점 사이 κ°„μ„ μ˜ 유무λ₯Ό μ €μž₯
  • ν–‰κ³Ό μ—΄μ˜ μΈλ±μŠ€λŠ” 두 정점을 ν‘œν˜„ν•¨
  • 무ν–₯ κ·Έλž˜ν”„μ˜ 경우 2차원 λ°°μ—΄ μœ„μ— κΈ°μšΈκΈ°κ°€ -1인 κ°€μƒμ˜ 직선이 μ‘΄μž¬ν•œλ‹€κ³  μƒκ°ν•˜κ³  이 직선을 κΈ°μ€€μœΌλ‘œ μ„œλ‘œ λŒ€μΉ­ν•˜λŠ” ν˜•νƒœλ‘œ 값이 λ“€μ–΄κ°€ 있음
  • κ°€μ€‘μΉ˜κ°€ μ—†λŠ” κ·Έλž˜ν”„μ˜ 경우 booleanν˜• 배열을 μƒμ„±ν•˜μ—¬, 간선이 μ‘΄μž¬ν•˜λ©΄ true, 간선이 μ—†μœΌλ©΄ falseλ₯Ό μ €μž₯ν•  수 있음
  • κ°€μ€‘μΉ˜κ°€ μžˆλŠ” κ·Έλž˜ν”„μ˜ 경우 λ°°μ—΄μ˜ 값에 κ°€μ€‘μΉ˜ 값을 μ €μž₯ν•  수 있음
  • 인접 행렬을 μ΄μš©ν•˜μ—¬ κ·Έλž˜ν”„λ₯Ό ν‘œν˜„ν•  경우 ν¬μ†Œ κ·Έλž˜ν”„(Sparse Graph) ν‘œν˜„ μ‹œ λ©”λͺ¨λ¦¬ λ‚­λΉ„κ°€ λ°œμƒν•  수 있음

    • ν¬μ†Œ κ·Έλž˜ν”„λž€ κ·Έλž˜ν”„μ˜ 정점 사이 간선이 비ꡐ적 적은 κ·Έλž˜ν”„λ₯Ό 의미 (<-> 밀집 κ·Έλž˜ν”„(Dense Graph))
  • 정점 쀑심 문제λ₯Ό ν•΄κ²°ν•  λ•Œ 적합
Java
public void adjacentMatrix() {
    int[][] vertexs = {{1, 2}, {1, 4}, {3, 2}, {4, 5}, {5, 3}};    // 인접 정점 정보
    int N;                                         // μ •μ μ˜ 개수
    boolean[][] adjMatrix = new boolean[N][N];     // κ°€μ€‘μΉ˜κ°€ μ—†λŠ” 인접 ν–‰λ ¬

    for (int i=0; i<vertexs.length; i++) {
        int from = vertexs[i][0];
        int to = vertexs[i][1];

        adjMatrix[from][to] = true;
        adjMatrix[to][from] = true;
    }
}

인접 리슀트(Adjacent List)

  • ν•˜λ‚˜μ˜ 정점에 λŒ€ν•œ 인접 정점듀을 μ—°κ²° λ¦¬μŠ€νŠΈμ— λ…Έλ“œ ν˜•νƒœλ‘œ μ €μž₯
  • 리슀트의 μΈλ±μŠ€λŠ” κ·Έλž˜ν”„μ˜ λͺ¨λ“  정점을 ν‘œν˜„ν•¨
  • 무ν–₯ κ·Έλž˜ν”„μ˜ 경우 μΈμ ‘ν•œ 정점 a와 bλ₯Ό ν‘œν˜„ν•  λ•Œ 정점 aλ₯Ό 리슀트의 μΈλ±μŠ€μ— λŒ€μΉ˜μ‹œν‚€κ³  정점 bλŠ” λ…Έλ“œ ν˜•νƒœλ‘œ 리슀트의 a μΈλ±μŠ€μ— μ €μž₯함. λ°˜λŒ€λ‘œ 정점 b μ—­μ‹œ 리슀트의 μΈλ±μŠ€μ— λŒ€μΉ˜μ‹œν‚€κ³  정점 aλŠ” λ…Έλ“œ ν˜•νƒœλ‘œ 리슀트의 b μΈλ±μŠ€μ— μ €μž₯ν•΄μ•Ό 함.

    • 무ν–₯ κ·Έλž˜ν”„μ˜ λ…Έλ“œ 수 = κ°„μ„ μ˜ 수 * 2
  • 유ν–₯ κ·Έλž˜ν”„λŠ” μ§„μΆœ μ •μ λ§Œ 리슀트의 μΈλ±μŠ€μ— λŒ€μΉ˜μ‹œν‚€κ³  μ§„μž… μ •μ λ§Œ λ…Έλ“œ ν˜•νƒœλ‘œ μ €μž₯ν•˜λ©΄ 됨

    • 유ν–₯ κ·Έλž˜ν”„μ˜ λ…Έλ“œ 수 = κ°„μ„ μ˜ 수
  • κ΅¬ν˜„μ΄ 쑰금 κΉŒλ‹€λ‘­μ§€λ§Œ 간선이 비ꡐ적 적은 ν¬μ†Œ κ·Έλž˜ν”„λ₯Ό ν‘œν˜„ν•˜κΈ°μ— 인접 행렬보닀 νš¨μœ¨μ μž„
  • 정점 쀑심 문제λ₯Ό ν•΄κ²°ν•  λ•Œ 적합
Java
class adjacentList() {

    static class Node {
        int vertex;    // 인접 정점 인덱슀
        Node link;     // 인접 정점 링크

        public Node(int vertex, Node link) {
            super();
            this.vertex = vertex;
            this.link = link;
        }
    }

    public void adjacentList() {
        int[][] vertexs = {{1, 2}, {1, 4}, {3, 2}, {4, 5}, {5, 3}};    // 인접 정점 정보
        int N;                                         // μ •μ μ˜ 개수
        Node[] adjList = new Node[N];     // κ°€μ€‘μΉ˜κ°€ μ—†λŠ” 인접 ν–‰λ ¬

        for (int i=0; i<vertexs.length; i++) {
            int from = vertexs[i][0];
            int to = vertexs[i][1];

            adjList[from] = new Node(to, adjMatrix[from]);
            adjList[to] = new Node(from, adjMatrix[to]);
        }
    }
}

κ°„μ„  리슀트(Edge List)

  • 두 정점에 λŒ€ν•œ κ°„μ„  자체λ₯Ό 객체둜 ν‘œν˜„ν•˜μ—¬ 리슀트둜 μ €μž₯
  • 간선을 ν‘œν˜„ν•˜λŠ” 두 정점(μ‹œμž‘ 정점, 끝 정점)의 정보λ₯Ό ν‘œν˜„

κ·Έλž˜ν”„ 탐색

λΉ„μ„ ν˜• 자료ꡬ쑰인 κ·Έλž˜ν”„μ˜ λͺ¨λ“  정점을 빠짐없이 νƒμƒ‰ν•˜λŠ” 기법

트리 μžλ£Œκ΅¬μ‘°μ™€ λ™μΌν•˜κ²Œ λ„ˆλΉ„ μš°μ„  탐색과 깊이 μš°μ„  탐색 기법이 μ‘΄μž¬ν•¨. 트리의 경우 μžμ‹ λ…Έλ“œμ—κ²Œ μ—°κ²°λœ λΆ€λͺ¨ λ…Έλ“œλŠ” 단 ν•œκ°œλ§Œ μ‘΄μž¬ν•˜λ―€λ‘œ ν•œ λ…Έλ“œμ— 도착할 수 μžˆλŠ” μΆœλ°œμ μ€ ν•΄λ‹Ή λ…Έλ“œμ˜ λΆ€λͺ¨λ…Έλ“œ ν•˜λ‚˜λΏμž„. ν•˜μ§€λ§Œ, κ·Έλž˜ν”„μ˜ 경우 ν•œ 정점에 μΈμ ‘ν•œ 정점이 μ—¬λŸ¬κ°œμΌ 수 μžˆμœΌλ―€λ‘œ ν•΄λ‹Ή μ •μ μœΌλ‘œ 도착할 수 μžˆλŠ” 좜발점이 μ—¬λŸ¬κ°œμž„.

λ”°λΌμ„œ κ·Έλž˜ν”„μ—μ„œμ˜ νƒμƒ‰μ—μ„œλŠ” 정점에 λŒ€ν•œ λ°©λ¬Έ 체크λ₯Ό ν•„μˆ˜μ μœΌλ‘œ 해주어야지 같은 정점을 μ€‘λ³΅ν•΄μ„œ λ°©λ¬Έν•˜μ§€ μ•ŠμŒ!!

  • 탐색 μ‹œμž‘ 정점뢀터 λͺ¨λ“  μΈμ ‘ν•œ 정점듀을 μ°¨λ‘€λŒ€λ‘œ λ°©λ¬Έν•œ λ’€, λ°©λ¬Έν–ˆλ˜ 정점을 μ‹œμž‘μ μœΌλ‘œ ν•˜μ—¬ λ‹€μ‹œ ν•΄λ‹Ή μ •μ μ˜ μΈμ ‘ν•œ 정점듀을 λͺ¨λ‘ λ°©λ¬Έν•˜λŠ” 방식
  • μΈμ ‘ν•œ 정점에 λŒ€ν•œ 탐색이 λλ‚˜μ•Ό ν•΄λ‹Ή μ •μ λ“€μ˜ μΈμ ‘ν•œ λ…Έλ“œλ₯Ό λ°©λ¬Έν•  수 μžˆμœΌλ―€λ‘œ μ„ μž… μ„ μΆœ ν˜•νƒœμ˜ 자료ꡬ쑰인 큐(Queue)λ₯Ό μ‚¬μš©ν•˜μ—¬ κ΅¬ν˜„ν•  수 있음
Java
// 인접 ν–‰λ ¬ κ·Έλž˜ν”„ bfs

public void adjMatrixBfs() {
    Queue<Integer> q = new LinkedList<>();      // μ‹œμž‘ 정점 μ €μž₯
    boolean[] visited = new boolean[N];         // 정점 λ°©λ¬Έ μ—¬λΆ€ κΈ°μ–΅
   
    q.offer(0);           // μ‹œμž‘ μ •μ μ˜ 인덱슀
    visited[0] = true;    // μ‹œμž‘ 정점 λ°©λ¬Έ 체크

    while(!q.isEmpty()) {
        int current = q.poll();
        
        System.out.println(current + " ");

        for (int i=0; i<N; i++) {     // κ·Έλž˜ν”„μ˜ λͺ¨λ“  정점에 λŒ€ν•΄
            if (!visited[i] && adjMatrix[current][i]) {     // 아직 λ°©λ¬Έ μ•ˆν–ˆκ³  ν˜„μž¬ 정점에 μΈμ ‘ν•œ 정점이면
                q.offer(i);           // 탐색을 μœ„ν•΄ 큐 맨 뒷단에 μ‚½μž…ν•˜κ³ 
                visited[i] = true;    // 미리 λ°©λ¬Έ μ²˜λ¦¬ν•¨μœΌλ‘œμ¨ 큐에 정점이 μ€‘λ³΅μ μœΌλ‘œ μ‚½μž…λ˜λŠ” 것을 λ§‰μŒ
            }
        }
    }
}
Java
// 인접 리슀트 κ·Έλž˜ν”„ bfs

public void adjListBfs() {
    Queue<Integer> q = new LinkedList<>();      // μ‹œμž‘ 정점 μ €μž₯
    boolean[] visited = new boolean[N];         // 정점 λ°©λ¬Έ μ—¬λΆ€ κΈ°μ–΅
   
    q.offer(0);           // μ‹œμž‘ μ •μ μ˜ 인덱슀
    visited[0] = true;    // μ‹œμž‘ 정점 λ°©λ¬Έ 체크

    while(!q.isEmpty()) {
        int current = q.poll();
        
        System.out.println(current + " ");

        for (Node i=adjList[current]; i!=null; i=i.link) {     // ν˜„μž¬ μ •μ μ˜ 인접 λ¦¬μŠ€νŠΈμ— μ €μž₯된 μΈμ ‘ν•œ 정점듀에 λŒ€ν•΄μ„œλ§Œ
            if (!visited[i.vertex]) {     // 아직 λ°©λ¬Έ μ•ˆν–ˆμœΌλ©΄
                q.offer(i.vertex);           // 탐색을 μœ„ν•΄ 큐 맨 뒷단에 μ‚½μž…ν•˜κ³ 
                visited[i.vertex] = true;    // 미리 λ°©λ¬Έ μ²˜λ¦¬ν•¨μœΌλ‘œμ¨ 큐에 정점이 μ€‘λ³΅μ μœΌλ‘œ μ‚½μž…λ˜λŠ” 것을 λ§‰μŒ
            }
        }
    }
}
  • μ‹œμž‘ μ •μ μ˜ ν•œ λ°©ν–₯으둜 갈 수 μžˆλŠ” κ²½λ‘œκ°€ μ‘΄μž¬ν•  λ•ŒκΉŒμ§€ κ³„μ†ν•΄μ„œ 깊게 탐색. 더 이상 λ°©λ¬Έν•  수 μžˆλŠ” κ²½λ‘œκ°€ μ—†μœΌλ©΄ λ§ˆμ§€λ§‰μœΌλ‘œ λ§Œλ‚¬λ˜ 갈림길이 있던 μ •μ μœΌλ‘œ λŒμ•„μ™€ λ‹€λ₯Έ λ°©ν–₯의 정점을 같은 λ°©μ‹μœΌλ‘œ κ³„μ†ν•΄μ„œ 깊게 νƒμƒ‰ν•˜λŠ” 방식
  • μΈμ ‘ν•œ μ •μ μ˜ μΈμ ‘ν•œ 정점을 λ¨Όμ € 깊게 λ°©λ¬Έν–ˆλ‹€κ°€ λ°©λ¬Έν•  λ…Έλ“œκ°€ 없을 경우 λŒμ•„μ™€μ„œ λ‹€λ₯Έ 인접 μ •μ μœΌλ‘œ 깊게 λ“€μ–΄κ°€μ•Ό ν•˜λ―€λ‘œ μž¬κ·€μ μœΌλ‘œ κ΅¬ν˜„ν•˜κ±°λ‚˜, ν›„μž… μ„ μΆœ ν˜•νƒœμ˜ 자료ꡬ쑰인 μŠ€νƒ(Stack)을 μ‚¬μš©ν•˜μ—¬ κ΅¬ν˜„ν•  수 있음
Java
// 인접 ν–‰λ ¬ κ·Έλž˜ν”„ bfs

public void dfs(int current, boolean[] visited) {
    visited[current] = true;       // ν˜„μž¬ 탐색 정점 λ°©λ¬Έ 처리

    System.out.println(current + " ");

    for (int i=0; i<N; i++) {     // κ·Έλž˜ν”„μ˜ λͺ¨λ“  정점에 λŒ€ν•΄
        // 아직 λ°©λ¬Έ μ•ˆν–ˆκ³  ν˜„μž¬ 정점에 μΈμ ‘ν•œ 정점이면 ν•΄λ‹Ή 정점을 λ‹€μ‹œ μ‹œμž‘μ μœΌλ‘œ μ‚Όμ•„ μΈμ ‘ν•œ 정점을 깊게 탐색
        if (!visited[i] && adjMatrix[current][i]) dfs(i, visited);
    }
}
Java
// 인접 리슀트 κ·Έλž˜ν”„ dfs

public void dfs(int current, boolean[] visited) {
    visited[current] = true;       // ν˜„μž¬ 탐색 정점 λ°©λ¬Έ 처리

    System.out.println(current + " ");

    for (Node i=adjList[current]; i!=null; i=i.link) {      // ν˜„μž¬ μ •μ μ˜ 인접 λ¦¬μŠ€νŠΈμ— μ €μž₯된 μΈμ ‘ν•œ 정점듀에 λŒ€ν•΄μ„œλ§Œ
        // 아직 λ°©λ¬Έ μ•ˆν–ˆμœΌλ©΄ ν•΄λ‹Ή 정점을 λ‹€μ‹œ μ‹œμž‘μ μœΌλ‘œ μ‚Όμ•„ μΈμ ‘ν•œ 정점을 깊게 탐색
        if (!visited[i.vertex]) dfs(i.vertex, visited);
    }
}