Knowledge/알고리즘

최단 경로 알고리즘

똑똑한망치 2024. 7. 4. 10:42
728x90
반응형

최단 경로 알고리즘

정점으로부터 정점까지의 모든 경로 중에서 가장 비용이 작은 경로

 

(1) Dijkstra

(정점을 고르는 정점 중심)

  • 출발 정점으로부터 다른 모든 정점까지의 거리를 선택된 정점을 지나서 가는 경우와 직접 가는 경우 중 최솟값을 갱신해가면서 가장 가까운 정점을 하나씩 선택된 정점에 편입시켜가며 최단 경로를 갱신
  • 음의 가중치를 허용하지 않음
기능 특징 시간 복잡도(노드 수 : V, 에지 수 : E)
출발 노드와 모든 노드간의 최단 거리 탐색 에지는 모두 양수 O(ElogV)
public class Dijkstra_Test {
 
    static class Edge implements Comparable<Edge> {
        int v, weight;
 
        public Edge(int v, int weight) {
            this.v = v;
            this.weight = weight;
        }
 
        @Override
        public int compareTo(Edge o) {
            return Integer.compare(this.weight, o.weight);
        }
        
    }
    
    public static void main(String[] args) throws IOException {
        
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int V = Integer.parseInt(st.nextToken());    // 정점의 개수
        int E = Integer.parseInt(st.nextToken());    // 간선의 개수
        int K = Integer.parseInt(br.readLine()) - 1;    // 시작 정점(번호를 1부터 input)
        final int INF = Integer.MAX_VALUE;
        
        // 인접 리스트 준비
        List<Edge>[] adj = new ArrayList[V];
        for (int i = 0; i < V; i++) 
            adj[i] = new ArrayList<>();
        
        // 간선 정보 입력
        for (int i = 0; i < E; i++)  {
            st = new StringTokenizer(br.readLine());
            adj[Integer.parseInt(st.nextToken()) - 1].add(new Edge(Integer.parseInt(st.nextToken()) - 1, 
                    Integer.parseInt(st.nextToken())));
        }
    
        // dist 배열
        int[] dist = new int[V];
        boolean[] checked = new boolean[V];
        
        // dist 배열을 INF로 초기화
        Arrays.fill(dist, INF);
        // 시작점은 0으로 변경
        dist[K] = 0;
        
        PriorityQueue<Edge> pQ = new PriorityQueue<>();
        pQ.add(new Edge(K, 0));
        Edge cur = null;
        
        while(!pQ.isEmpty()) {
            // check되지 않았으면서,
            // 현재(i)정점으로부터 dist 값이 제일 작은 정점(j)의 번호 찾기
            cur = pQ.poll();
            if(checked[cur.v]) continue;
            
            // 찾은 정점(j)으로부터 갈 수 있는 경로가 이미 알고 있는 dist보다 작다면 갱신
            // index가 가지고 있는 모든 간선을 검사
            for (Edge next : adj[cur.v]) {
                // check되지 않았으면서 다음 노드 까지의 거리가
                // 나까지 거리 + 나로부터 다음 노드까지 거리보다 작다면 갱신 
                if(!checked[next.v] && dist[next.v] > dist[cur.v] + next.weight) {
                    dist[next.v] = dist[cur.v] + next.weight;
                    pQ.add(new Edge(next.v, dist[next.v]));
                }
            }
            
            // 체크 완료
            checked[cur.v] = true;
        }
        
        for (int i = 0; i < V; i++) {
            if(dist[i] == INF) System.out.println("INF");
            else System.out.println(dist[i]);
        }
    }
}

 

 

(2) Bellman-Ford

(간선을 고르는 간선 중심)

(n-1)번 반복하기

기능 특징 시간 복잡도(노드수 : V, 에지 수 : E)
특정 출발 노드에서 다른 모든 노드까지의 최단 경로 탐색 - 음수 가중치 에지가 있어도 수행할 수 있음
- 전체 그래프에서 음수 사이클의 존재 여부를 판단할 수 있음
O(VE)

 

 

Floyd-Warshall

기능 특징 시간 복잡도(노드 수 : V)
모든 노드 간에 최단 경로 탐색 - 음수 가중치 에지가 있어도 수행할 수 있음
- 동적 게획법의 원리를 이용해 알고리즘에 접근
O(V^3)

 

[도출한 플로이드-워셜 점화식]

D[S][E] = Math.min(D[S][E], D[S][K] + D[K][E])

public class FloydWarshall_Test {
 
    static final int INF = 987654321;
 
    public static void main(String[] args) {
 
        Scanner sc = new Scanner(System.in);
 
        int N = sc.nextInt();
        int[][] adjMatrix = new int[N][N];
 
        for (int i = 0; i < N; ++i) {
            for (int j = 0; j < N; ++j) {
                adjMatrix[i][j] = sc.nextInt();
                // 자기자신으로의 인접 정보가 아니고 인접해있지 않다면 INF로 채우기
                if (i != j && adjMatrix[i][j] == 0) {
                    adjMatrix[i][j] = INF;
                }
            }
        }
 
        // 경유지 -> 출발지 -> 도착지
        for (int k = 0; k < N; ++k) {
            for (int s = 0; s < N; ++s) {
                // 출발지와 경유지가 같다면 pass
                if (s == k) continue; 
                
                for (int e = 0; e < N; ++e) {
                    // 경유지와 목적지가 같거나 출발지가 곧 목적지라면 pass
                    if (s == e || k == e) continue; 
                    
                    // 경유한 길이 최단 거리라면 갱신
                    if (adjMatrix[s][k] + adjMatrix[k][e] < adjMatrix[s][e]) 
                        adjMatrix[s][e] = adjMatrix[s][k] + adjMatrix[k][e];
                }
            }
            for(int i=0; i<N; ++i) {
                for(int j=0; j<N; ++j) {
                    System.out.print(adjMatrix[i][j]+"\t");
                }
                System.out.println();
            }
            System.out.println("=====================================");
    
        }
 
    }
 
}

 

 

반응형

'Knowledge > 알고리즘' 카테고리의 다른 글

위상 정렬 (topology sort)  (0) 2024.07.10
유니온 파인드 알고리즘 (Union-find)  (1) 2024.07.06
유클리드 호제법  (0) 2024.07.01
오일러 피  (0) 2024.06.28
너비 우선 탐색 (BFS : Breadth-First Search)  (0) 2024.06.24