전체 보기
🚀

다익스트라

작성일자
2024/02/01
태그
SUB PAGE
프로젝트
30일 알고리즘
책 종류
1 more property

정의

하나의 시작 노드로부터 다른 모든 노드까지의 최단 거리를 구하는 알고리즘

특징

다익스트라: 한 지점에서 모든 지점까지 플로이드 워셜: 모든 지점에서 다른 모든 지점까지
A*: 100% 정확한 최단거리 내지 않아도 되고, 정점의 개수가 많아 현실적으로 다익스트라 쓰기 어려운 경우 사용할 수 있는 근사 알고리즘
시간복잡도는 O(ElogE) (E: 에지 수)
에지 한 개당 우선순위 큐에 최대 한 개의 원소가 추가될 수 있기 때문
우선순위 큐에 N개의 원소를 추가하는 시간 복잡도 → O(NlogN)
우선순위 큐로 구현 가능
단계마다 방문하지 않은 노드 중 가장 최단 거리 짧은 노드 선택한 뒤에, 그 노드를 거쳐 가는 경우를 확인해 최단 거리를 갱신
최단 거리 배열에서 현재 값이 가장 작은 노드 고르는 이유)
현재 선택한 노드의 최단 거리가 앞으로 변경될 일 없는 고정된 결과여야 해서. 만일 고정된 결과가 아니라면, 다른 노드들에게 잘못된 최단 거리를 전파하게 됨.
현재 최단 거리 배열에서 최단 거리가 가장 짧은 노드는 값이 더이상 변경될 일이 없다고 확신할 수 있는 이유)
노드의 최단 거리는 다른 노드의 최단 거리 배열의 값(+ 에지 가중치)과 비교해 둘 중 작은 값으로 업데이트될 텐데, 현재 노드의 최단 거리 배열의 값이 배열에서 가장 작다면 다른 어떤 값과 비교해도 항상 더 작아서 업데이트될 일이 절대 없기 때문
예시)
에지의 가중치가 모두 양수일 때만 사용 가능, 방향/무방향 여부는 상관 없이 사용 가능
가중치가 음수인 에지가 있으면 사용할 수 없는 이유)
현재 갈 수 있는 노드 중 가장 가까운 노드까지의 거리를 확정할 수 없기 때문
예시)

추상화한 과정

1.
그래프를 인접 리스트로 구현하기
2.
최단 거리 배열 초기화하기
출발 노드는 0, 이외의 노드는 무한(99_999_999 같이 적당히 큰 값)으로 초기화
3.
최단 거리 배열에서 현재 값이 가장 작은 노드 고르기
4.
최단 거리 배열 업데이트하기
현재 선택된 노드에 연결된 에지의 값을 바탕으로 현재 노드와 연결된 노드의 최단거리 값 업데이트
최단거리 값을 Min(선택 노드의 최단 거리 배열의 값 + 에지 가중치, 연결된 노드의 최단 거리 배열의 값) 으로 업데이트
5.
모든 노드가 처리될 때까지 과정 3~4를 반복해 최단 거리 배열 완성하기
과정 4에서 선택 노드가 될 때마다 다시 선택되지 않도록 방문 배열 만들어 처리

구체화한 과정

1.
우선순위 큐에 (거리,노드) 꼴로 (0, 시작점)을 추가, 최단 거리 배열에 시작점은 0으로, 나머지는 큰 수로 초기화
2.
우선순위 큐에서 거리가 가장 작은 원소(노드)를 선택 (pop)
해당 원소의 거리가 최단 거리 테이블에 있는 값과 다를 경우 3번 과정을 수행하지 않고 넘어감
원소의 거리가 최단 거리 테이블에 있는 값과 다르다는 건 해당 원소가 우선순위 큐에 들어온 이후로 더 짧은 경로가 등장해서 현재는 쓸모가 없어진 원소라는 의미임
우선순위 큐에 원소가 들어오는 상황은, 항상 거리가 더 줄어들었을 때 최단 거리 테이블을 갱신하며 우선순위 큐에 들어온 상황이기 때문임
따라서 원소의 거리가 최단 거리 테이블에 있는 값과 다를 땐 해당 원소의 거리는 최단 거리 테이블에 있는 값보다 무조건 더 큼 → 의미 없는 값
3.
선택한 노드와 이웃한 노드들에 대해
현재 본인의 최단 거리 테이블 값(최단 거리)보다
선택한 노드를 거쳐가는 것이 더 작은 값을 가질 경우
최단 거리 테이블 값을 갱신하고 우선순위 큐에 (거리,노드)를 추가
4.
우선순위 큐가 빌 때까지 2~3번 과정을 반복

코드 1

0.
가중치 있는 그래프를 인접리스트로 표현하기
1.
우선순위 큐에 (거리, 노드)꼴로 (0, 시작점)을 추가, 최단 거리 배열에 시작점은 0으로, 나머지는 큰 수로 초기화
2.
우선순위 큐에서 거리가 가장 작은 원소를 pop하여 선택하고, 원소의 거리가 최단 거리 배열의 값과 같은지 확인
3.
선택한 노드와 이웃한 노드들의 최단 거리 갱신되어야 하면, 최단 거리 테이블 값 변경하고 우선순위 큐에 (거리, 노드)를 추가
최단 거리 계산 : min(선택 노드의 최단 거리+에지 가중치, 이웃 노드의 최단 거리)
최단 거리 갱신 조건 : "선택 노드의 최단 거리+에지 가중치"가 "이웃 노드의 최단 거리"보다 더 작을 때만 최단 거리 갱신
4.
우선순위 큐 빌 때까지 2,3을 반복
private static final int INFINITE = 99_999_999; private static class Edge implements Comparable<Edge> { int weight; int node; private Edge(int weight, int node) { this.weight = weight; this.node = node; } public int compareTo(Edge e) { if (this.weight > e.weight) { return 1; } else { return -1; } } } // numbers[edge+1][3]는 edge개의 줄에 걸쳐 각 간선을 나타내는 세 개의 정수(u, v, w)로 구성. 세 정수는 u에서 v로 가는 가중치 w인 간선을 의미 private static void solution(int vertex, int edge, int start, int[][] numbers) { // 0. 가중치 있는 그래프를 인접리스트로 표현하기 ArrayList<Edge>[] graph = new ArrayList[vertex + 1]; for (int i = 1; i <= vertex; i++) { graph[i] = new ArrayList<>(); } for (int i = 1; i <= edge; i++) { int u = numbers[i][0]; int v = numbers[i][1]; int w = numbers[i][2]; graph[u].add(new Edge(w, v)); } // 1. 우선순위 큐에 (거리, 노드)꼴로 (0, 시작점)을 추가, 최단 거리 배열에 시작점은 0으로, 나머지는 큰 수로 초기화 PriorityQueue<Edge> pq = new PriorityQueue<>(); pq.add(new Edge(0, start)); int[] distance = new int[vertex + 1]; for (int i = 1; i <= vertex; i++) { distance[i] = INFINITE; if (i == start) { distance[i] = 0; } } while (!pq.isEmpty()) { // 2. 우선순위 큐에서 거리가 가장 작은 원소를 pop하여 선택하고, 원소의 거리가 최단 거리 배열의 값과 같은지 확인 Edge select = pq.poll(); if (select.weight != distance[select.node]) { continue; } // 3. 선택한 노드와 이웃한 노드들의 최단 거리 갱신되어야 하면, 최단 거리 테이블 값 변경하고 우선순위 큐에 (거리, 노드)를 추가 // 최단 거리 갱신 조건 : "선택 노드의 최단 거리+에지 가중치"가 "이웃 노드의 최단 거리"보다 더 작을 때만 최단 거리 갱신 for (Edge near : graph[select.node]) { if (distance[select.node] + near.weight >= distance[near.node]) { continue; } distance[near.node] = distance[select.node] + near.weight; pq.add(new Edge(distance[near.node], near.node)); } } }
Java
복사

코드 2

위 코드가 잘 이해되신 분들을 위한 구현이 더 간단하지만 조금 덜 직관적인 코드
0.
가중치 있는 그래프를 인접리스트로 표현하기
1.
우선순위 큐에 (거리, 노드)꼴로 (0, 시작점)을 추가, 최단 거리 배열은 큰수로 초기화
2.
우선순위 큐에서 거리가 가장 작은 원소를 선택(pop)하고, 한 번도 선택된 적이 없는 노드라면 최단 거리 배열에 원소의 가중치 값 넣기
3.
선택한 노드와 이웃한 노드가 한 번도 선택된 적이 없는 노드라면, 우선순위 큐에 (거리, 노드)를 추가
4.
우선순위 큐 빌 때까지 2,3을 반복
달라진 점
최단 거리 배열을 굳이 매번 갱신하지 않고,
최단 거리임이 확정된 원소만 값을 저장
// 위는 동일 // 1. 우선순위 큐에 (거리, 노드)꼴로 (0, 시작점)을 추가, 최단 거리 배열은 큰수로 초기화 PriorityQueue<Edge> pq = new PriorityQueue<>(); pq.add(new Edge(0, start)); int[] distance = new int[vertex + 1]; for (int i = 1; i <= vertex; i++) { distance[i] = INFINITE; // start값도 infinite로 두기 } while (!pq.isEmpty()) { // 2. 우선순위 큐에서 거리가 가장 작은 원소를 선택(pop)하고, 한 번도 선택된 적이 없는 노드라면 최단 거리 배열에 원소의 가중치 값 넣기 Edge select = pq.poll(); if (distance[select.node] != INFINITE) { continue; } distance[select.node] = select.weight; // 3. 선택한 노드와 이웃한 노드가 한 번도 선택된 적이 없는 노드라면, 우선순위 큐에 (거리, 노드)를 추가 for (Edge near : graph[select.node]) { if (distance[near.node] != INFINITE) { continue; } pq.add(new Edge(distance[select.node] + near.weight, near.node)); } }
Java
복사
참고