코딩하는 해달이

[알고리즘] 위상 정렬 본문

개인 공부/알고리즘&자료구조

[알고리즘] 위상 정렬

코딩하는 해달 2024. 9. 20. 14:06

위상 정렬

위상 정렬은 주로 작업 순서를 정하거나 의존성 관계를 해결할 때 사용하는 알고리즘으로 정점들의 선행 순서를 위배하지 않으면서 모든 정점을 나열하는 알고리즘이다.

특징

  • 비순환 방향 그래프에서만 적용이 가능하다.
  • 정렬 결과가 여러가지 일 수 있다.
  • 사이클이 있는 그래프에서는 위상 정렬이 불가능하다.
      - 사이클 : 한 정점에서 출발하여 간선과 정점을 지나 다시 출발했던 정점으로 돌아오는 것

알고리즘 구현 방법

  • DFS(깊이 우선 탐색) 기반 방법 : 그래프를 DFS로 탐색하며 탐색이 끝나느 정점부터 스택에 삽입, 스택에서 꺼내는 순서가 정렬의 결과
  • 진입차수 기반 방법 : 진입차수가 0인 정점을 큐에 넣고 하나씩 꺼내면서 간선을 제거, 큐에서 꺼내는 순서가 정렬의 결과

시간 복잡도

O(V + E) (V : 정점의 수, E : 간선의 수)

예제(진입차수 기반 방법)

비순환 방향 그래프 예시

위의 그래프를 이용해 위상정렬을 순서대로 진행해보자

1. 진입차수를 확인한다(진입 차수 : 해당 정점을 향하는 간선의 수)

2. 큐에 진입차수가 0인 정점을 넣는다.

3. 큐에서 순서대로 정점을 꺼내고 해당 정점의 간선을 제거한다.

4. 다시 처음으로 돌아가서 반복한다.

import java.util.*;

public class TopologicalSort {
    private int V; // 정점의 수
    private ArrayList<ArrayList<Integer>> adj; // 인접 리스트

    public TopologicalSort(int v) {
        V = v;
        adj = new ArrayList<>(V);
        for (int i = 0; i < V; i++) {
            adj.add(new ArrayList<>());
        }
    }

    // 간선 추가 (v -> w)
    public void addEdge(int v, int w) {
        adj.get(v).add(w);
    }

    // 위상 정렬 수행
    public ArrayList<Integer> topologicalSort() {
        int[] inDegree = new int[V]; // 각 정점의 진입차수
        ArrayList<Integer> result = new ArrayList<>(); // 정렬 결과

        // 모든 정점의 진입차수 계산
        for (int i = 0; i < V; i++) {
            for (int node : adj.get(i)) {
                inDegree[node]++;
            }
        }

        // 진입차수가 0인 정점을 큐에 삽입
        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < V; i++) {
            if (inDegree[i] == 0) {
                queue.offer(i);
            }
        }

        // 위상 정렬 알고리즘 수행
        while (!queue.isEmpty()) {
            int v = queue.poll();
            result.add(v);

            // 해당 정점과 연결된 모든 정점들의 진입차수를 1 감소
            for (int adj : adj.get(v)) {
                if (--inDegree[adj] == 0) {
                    queue.offer(adj);
                }
            }
        }

        // 모든 정점을 방문하지 못했다면 사이클이 존재한다는 의미
        if (result.size() != V) {
            System.out.println("그래프에 사이클이 존재합니다.");
            return new ArrayList<>();
        }

        return result;
    }

    public static void main(String[] args) {
        TopologicalSort g = new TopologicalSort(8);
        g.addEdge(1, 2);
        g.addEdge(1, 5);
        g.addEdge(2, 3);
        g.addEdge(3, 4);
        g.addEdge(4, 6);
        g.addEdge(5, 3);
        g.addEdge(5, 6);
        g.addEdge(6, 7);

        ArrayList<Integer> sortedOrder = g.topologicalSort();
        System.out.println("위상 정렬 결과: " + sortedOrder);
    }
}
반응형
Comments