반응형
Dijkstra 란?
- 한 노드에서 다른 모든 노드까지의 최단거리를 구하는 알고리즘
- 단, 가중치에 음수가 있으면 사용할 수 없음
- 시간복잡도 : O(E logN) (N:노드의 수, E: 간선의 수)
- 다음과 같은 그래프가 있다고 가정
- 1번 노드에서 2, 3, 4, 5번 노드까지의 최단거리를 모두 구해야 함
- dist[1~5] = [0, ∞, ∞, ∞, ∞]로 초기화 후 시작
- 1번 노드에서 갈 수 있는 노드 = 2, 3, 4
- 1(시작노드) -> 2로 갈 때 드는 비용 ( = dist[1] + (1->2) )과 지금까지 2로가는 최소 비용 (=dist[2])를 비교해서 지금까지 2로가는 최소비용이 더 크다면 dist[2] = dist[1] + (1->2)로 갱신
- 이 방식으로 1번노드에서 갈 수 있는 노드들에 대한 비교를 마치면, dist = [0, 1, 2, 6, ∞]
- 1번 노드를 제외하고, 남은 것중 dist값이 가장 작은 노드 = 2
- 2번 노드는 이제 최단 거리가 확정됨
- 2번 노드에서 갈 수 있는 노드 = 4, 5
- dist[4]와 dist[2] + (2->4)를 비교했을 때, dist[4]가 더 큼
- 이 뜻은 지금까지 4번 노드로 가는 방법보다, 2번 노드를 경유해 4번 노드로 가는 방법이 더 좋다는 뜻 => 값 갱신
- 이 방식으로 2번노드에서 갈 수 있는 노드들에 대한 비교를 마치면, dist = [0, 1, 2, 5, 16]
- 1, 2번 노드를 제외하고, 남은 것 중 dist값이 가장 작은 노드 = 3
- 3번 노드 최단 거리 확정
- 3번 노드에서의 비교를 마치면, dist = [0, 1, 2, 4, 16]
- 4번 노드에서의 비교를 마치면, dist = [0, 1, 2, 4, 12]
- 5번 노드에서의 비교를 마치면, dist = [0, 1, 2, 4, 12]
- 1번 노드에서 1, 2, 3, 4, 5번 노드로 가는데 걸리는 최소 비용 = 0, 1, 2, 4, 12
시간복잡도 계산 (Priority Queue를 이용한 시간 줄이기)
- N : 노드의 수, E : 간선의 수 라고 했을때, 이 알고리즘의 시간복잡도를 계산
- 현재 노드에서 갈 수 있는 노드 중 가장 작은 dist 값을 가진 노드를 찾기 위해서는 최악의 경우 O(N)의 시간이 걸림
- 모든 노드들을 한 번씩 들려야 되므로 O(N)이 걸림
- 따라서 시간복잡도는 O(N^2)
- Priority Queue(우선순위 큐)를 사용하면 시간을 줄일 수 있음 => Priority Queue에서 삽입, 삭제의 시간 복잡도는 O(log N)
- Priority Queue를 사용해 현재 가장 작은 dist를 가진 노드를 찾는다면 O(log N)의 시간이 걸림
- 이런식으로 다음 노드를 구한다면 이 노드에서 다음 노드까지의 dist를 계산해야 함
- 이 작업이 최악의 경우 모든 간선을 확인해야 되기 때문에 O(E)의 시간이 걸림
- 따라서 시간복잡도는 O(E logN)
구현
이 문제를 다익스트라를 통해 해결하면서 구현해봄
코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class p1753 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken());
int e = Integer.parseInt(st.nextToken());
st = new StringTokenizer(br.readLine());
int start = Integer.parseInt(st.nextToken());
ArrayList<Node>[] connections = new ArrayList[n + 1];
for(int i = 1 ; i <= n ; i ++) {
connections[i] = new ArrayList<>();
}
for(int i = 0 ; i < e ; i ++) {
st = new StringTokenizer(br.readLine());
int x = Integer.parseInt(st.nextToken());
int y = Integer.parseInt(st.nextToken());
int z = Integer.parseInt(st.nextToken());
connections[x].add(new Node(y, z));
}
/*
입력 => connections
5 6 [1] = { (2,2), (3,3) }
1 [2] = { (3,4), (4,5) }
5 1 1 [3] = { (4,6) }
1 2 2 [4] = {}
1 3 3 [5] = { (1,1) }
2 3 4
2 4 5
3 4 6
*/
int[] dist = new int[n + 1];
Arrays.fill(dist, Integer.MAX_VALUE);
dist[start] = 0;
boolean[] visited = new boolean[n + 1];
PriorityQueue<PqFormat> pq = new PriorityQueue<>();
pq.add(new PqFormat(start, 0));
while(!pq.isEmpty()) {
PqFormat now = pq.poll();
if(visited[now.index] == true) continue;
visited[now.index] = true;
for(Node node : connections[now.index]) {
// now.index : 현재 노드 번호 now.dist : 현재 노드의 dist
// node.next : 현재 노드와 연결된 다음 노드 번호
// node.cost : 현재 노드에서 다음 노드까지 가는데의 비용
// dist[다음노드] > dist[현재노드] + 현재노드에서 다음노드 가는 비용이면 갱신
if(dist[node.next] > dist[now.index] + node.cost) {
dist[node.next] = dist[now.index] + node.cost;
pq.add(new PqFormat(node.next, dist[node.next]));
}
}
}
for(int i = 1 ; i <= n ; i ++) {
if(dist[i] == Integer.MAX_VALUE) {
System.out.println("INF");
} else {
System.out.println(dist[i]);
}
}
}
static class Node {
int next, cost;
public Node(int next, int cost) {
this.next = next;
this.cost = cost;
}
}
static class PqFormat implements Comparable<PqFormat>{
int index, dist;
PqFormat(int index, int dist) {
this.index = index;
this.dist = dist;
}
@Override
public int compareTo(PqFormat o) {
// dist 기준 오름차순 정렬
return this.dist - o.dist;
}
}
}
반응형
'JAVA > 알고리즘 개념 정리' 카테고리의 다른 글
[JAVA] 플로이드-워셜 알고리즘 (Floyd-Warshall) (0) | 2022.06.05 |
---|---|
[JAVA] 벨만포드 알고리즘 (Bellman-Ford) (0) | 2022.06.05 |
[JAVA] DFS, BFS (0) | 2022.05.31 |
[JAVA] 다양한 정렬 알고리즘 개념 및 시간 복잡도 비교 (0) | 2022.05.28 |
[JAVA] 재귀함수 (0) | 2021.08.17 |