다익스트라 알고리즘은 그래프에서 출발 노드와 모든 노드 간의 최단 거리를 찾는 데 사용되는 효율적인 알고리즘 중 하나이다. 이 알고리즘은 양의 가중치를 가진 간선으로 이루어진 그래프에서 사용할 수 있다.
시간 복잡도는 O(ElogV)이다.
알고리즘 동작 원리
- 시작점 설정: 최단 경로를 찾을 시작점을 선택한다.
- 거리 초기화: 시작점을 제외한 모든 정점까지의 거리를 무한대로 설정하고, 시작점의 거리를 0으로 설정한다.
- 우선순위 큐 초기화: 우선순위 큐를 사용하여 현재까지의 최단 거리를 가진 정점을 방문한다. 시작점을 우선순위 큐에 삽입한다.
- 최단 경로 갱신: 우선순위 큐에서 가장 최단 거리를 가진 정점을 추출하고, 해당 정점과 이웃한 정점들의 거리를 갱신한다. 새로운 거리가 더 짧으면 해당 정점의 거리를 갱신하고, 우선순위 큐에 새로운 거리를 기반으로 삽입한다. 즉, 방문한 정점의 최단거리가 아니라 방문하게 될 정점의 최단거리를 구하게 된다.
- 반복 수행: 우선순위 큐가 비어질 때까지 4단계를 반복합니다. 이때, 우선순위 큐에서 추출한 정점은 최단 경로가 확정된 정점으로 간주된다.
최단 거리와 최단 경로
import java.util.*;
public class Dijkstra {
private static final int INF = Integer.MAX_VALUE;
public static void dijkstra(List<List<Node>> graph, int start) {
int n = graph.size();
// 각 정점까지의 최단거리
int[] distance = new int[n];
Arrays.fill(distance, INF);
// 최단 경로에서 각 정점의 이전 정점
int[] previous = new int[n];
Arrays.fill(previous,-1);
// 방문 여부
boolean[] visited = new boolean[n];
PriorityQueue<Node> priorityQueue = new PriorityQueue<>(Comparator.comparingInt(node -> node.distance));
priorityQueue.add(new Node(start, 0));
distance[start] = 0;
while (!priorityQueue.isEmpty()) {
Node poll = priorityQueue.poll();
int curV = poll.vertex;
// 현재 정점까지의 거리
int curD = poll.distance;
if (visited[curV]) {
continue;
}
visited[curV] = true;
for (Node neighbor : graph.get(curV)) {
int newDistance = curD + neighbor.distance;
if (newDistance < distance[neighbor.vertex]) {
previous[neighbor.vertex] = curV;
distance[neighbor.vertex] = newDistance;
// 정점까지의 새로운 거리 값으로 노드를 생성한다.
priorityQueue.add(new Node(neighbor.vertex, newDistance));
}
}
}
// 결과 출력
System.out.println("최단 경로 결과:");
for (int i = 1; i < n; i++) {
List<Integer> path = getPath(previous, i);
System.out.println("정점 " + i + ": 최단 거리: " + distance[i] + ", 경로: "+ path);
}
}
public static List<Integer> getPath(int[] previous, int destination) {
List<Integer> path = new ArrayList<>();
for (int at = destination; at != -1; at = previous[at]) {
path.add(at);
}
Collections.reverse(path);
return path;
}
static class Node {
int vertex;
int distance;
Node(int vertex, int distance) {
this.vertex = vertex;
this.distance = distance;
}
}
}
K번째 최단 거리
import java.util.List;
import java.util.PriorityQueue;
public class KthDijkstra {
public static int[] kthDijkstra(List<List<Node>> graph, int start, int k) {
int n = graph.size();
// 각 정점으로 가는 거리들을 담는 배열
PriorityQueue<Integer>[] distQueue = new PriorityQueue[n + 1];
for (int i = 0; i <= n; i++) {
// k 번째 부터 최단 거리 까지 오름차순 으로 담길 수 있도록 한다.
distQueue[i] = new PriorityQueue<>(k, (o1, o2) -> o2 - o1);
}
// 현재 노드에서 출발하여 아직 방문하지 않은 노드까지의 거리 정보를 저장하는 큐
PriorityQueue<Node> pq = new PriorityQueue<>();
pq.offer(new Node(start, 0));
distQueue[start].add(0);
// 탐색 시작
while (!pq.isEmpty()) {
Node poll = pq.poll();
int curV = poll.vertex;
int curD = poll.distance;
List<Node> neighbors = graph.get(curV);
for (Node neighbor : neighbors) {
int nextV = neighbor.vertex;
int nextD = neighbor.distance + curD;
// 저장된 경로가 K개가 안 될 때는 그냥 추가하기
PriorityQueue<Integer> distanceQ = distQueue[nextV];
if (distanceQ.size() < k) {
distanceQ.offer(nextD);
pq.offer(new Node(nextV, nextD));
/*
저장된 경로가 K개이고 현재 가장 큰 값보다 작을 때,
k번째 최단 거리를 갱신한다.
[15, 10] : 13 => [13, 10]
*/
} else if (distanceQ.peek() > nextD) {
distanceQ.poll();
distanceQ.offer(nextD);
pq.offer(new Node(nextV, nextD));
}
}
}
// k번째 최단 거리가 담길 배열
int[] result = new int[n];
for (int i = 1; i < n; i++) {
PriorityQueue<Integer> queue = distQueue[i];
if (queue.size() != k) {
result[i] = -1;
} else {
result[i] = queue.poll();
}
}
return result;
}
}
728x90
'자료구조 & 알고리즘' 카테고리의 다른 글
플로이드-와샬 알고리즘 (0) | 2024.02.17 |
---|---|
벨만 포드 알고리즘 (0) | 2024.02.17 |
위상 정렬 알고리즘 (0) | 2024.02.16 |
퀵선택 알고리즘 (0) | 2024.02.15 |
유니온-파인드 알고리즘 (0) | 2024.02.05 |