본문 바로가기
OLD_알고리즘

Solving Skill ] 최단 경로

by 달승 2021. 3. 22.

최단 경로

"가장 짧은 경로를 찾는 알고리즘"



✔ '길 찾기' 문제 혹은 '가장 짧은 경로를 찾는' 문제라고 불립니다.

✔ 보통 Graph를 이용해 표현합니다.

✔ 실제 코테에서는 단순히 최단 거리를 출력하도록 요구하는 문제가 출제된다.

✔✔  Greedy와 DP의 한 유형이라고 할 수 있습니다.

 

 

🌈 최단 경로 알고리즘 종류

      다익스트라 최단 경로 알고리즘 ***

      플로이드 워셜 알고리즘 ***

      벨만 포드 알고리즘

 

 


🏇 다익스트라 최단 경로 알고리즘

         "여러 개의 노드 중, 특정한 노드에서 출발하여 다른 노드로 가는 최단 경로를 구하는 알고리즘"

 

✔ 0보다 작은 값의 간선이 없을 때, 정상적으로 실행이 가능합니다.

매번 가장 작은 비용의 노드를 선택하는 과정을 반복하기 때문에 'Greedy'로 분류됩니다.

 

 

🐸 실행 과정

     ① 출발 노드를 설정한다.

     ② 최단 거리 테이블*을 초기화한다.

     ③ 방문하지 않은 노드 중, 최단 거리가 가장 짧은 노드를 선택한다.

     해당 노드를 거쳐 다른 노드로 가는 비용을 계산해 최단 거리 테이블을 갱신한다.

     ⑤ 위 과정에서③, 를 반복한다.

  최단 거리 테이블이란?
     : 1차원 리스트로 최단 거리를 갱신하는 테이블

 

✔ 아래의 방법을 반복합니다.

  ✔ 초기 상태에는 거리를 무한으로 초기화합니다.

  ✔ 출발 노드인 1의 거리는 0입니다.(자기 자신에서 출발하므로)

  ✔ 노드 1에서 이동할 수 있는 방법은 총 세 가지입니다.
     각 거리로 최단 거리 테이블을 초기화합니다.
  ✔ 테이블에서 거리가 최단인 노드는 '4'이므로 STEP3에서는 노드 4부터 최단 거리를 check합니다.

  ✔ 
초기화할 때, 거리가 같은 경우에는 번호가 작은 노드를 선택합니다.

** 그림의 테이블에는 노드 6이 빠져있습니다. 그리다보니 빼먹음...

  암튼 출발부터 가장 최단 거리를 선택해 거리를 계산하므로 마지막 노드는 확인할 필요가 없습니다.

 

 

 

|   방법 1. 간단한 알고리즘

 

  ⏱ 간단한 다익스트라 알고리즘의 시간복잡도

     :  Θ(V^2)

        * 이때 V는 노드의 개수

 

 

🐸 실행 과정

✔ 처음에는 1차원 배열을 선언.

✔ 단계마다 '방문하지 않는노드 중, 최단 거리가 가장 짧은 노드를 선택'하기 위해 단계마다 1차원 리스트를 순차 탐색(원소를 모두 확인)

 

 

💻 코드 예시

     ** 전체 노드의 개수가 5,000개 이하일 때,

#include <iostream>
#include <vector>
using namespace std;

/*
 *코드 참고 : https://github.com/ndb796/python-for-coding-test/blob/master/9/1.cpp
 */

// 노드의 개수(N), 간선의 개수(M), 시작 노드 번호(Start)
int n, m, start;

vector<pair<int, int>> graph[100001]; // 노드 정보 저장
bool visited[100001]; // 방문 check
int d[100001]; // 최단 거리 테이블

// 방문하지 않은 노드 중에서, 가장 최단 거리가 짧은 노드의 번호를 반환
int getSmallNode(){
	int min_value = 1e9;
	int index = 0;
	
	for(int i = 0; i <= n; i++){
		if(d[i] < min_value && !visited[i]){
			min_value = d[j];
			index = i;
		}
	}
	return index;
}

void dijkstra(int start){
	// 시작 노드 초기화 & 방문처리
	d[start] = 0;
	visited[start] = true;
	for(int i = 0; i < graph[start].size(); i++){
		d[graph[start][i].first] = graph[start][i].second;
	}
	
	// 시작 노드 제외한 전체 n - 1까지 반복
	for(int i = 0; i < n - 1; i++){
		// 현재 최단 거리 짧은 노드 꺼내서 방문처리
		int now = getSmallNode();
		visited[now] = true;
		
		// 현재 노드와 연결된 다른 노드 확인
		for(int j = 0; j < graph[now].size(); i++){
			int cost = d[now] + graph[now][j].second;
			
			// 현재 노드를 거쳐서 다른 노드로 이동하는 거리가 더 짧은 경우
			if(cost < d[graph[now][j].first]) d[graph[now][j]].first = cost;
		}
	}
	
}

int main() {
	
	cin >> n >> m >> start;
	
	// 간선 정보 입력
	for(int i = 0; i < n; i++){
		int a, b, c;
		cin >> a >> b >> c;
        // a번 노드에서 b번 노드로 가는 비용이 c라는 의미
		graph[a].push_back({b, c});
        // graph[a].push_back(make_pair(b, c)); 도 가능하당
	}
	
	d[100001] = {1e9,}; // 무한 초기화
	
	// 다익스트라 수행
	dijkstra(start);
	
	for(int i = 1; i <= n; i++){
		if(d[i] != 1e9) cout << d[i] << endl;
	}
	
	return 0;
}

 

 

|   방법 2. 개선된 알고리즘

 

  ⏱ 간단한 다익스트라 알고리즘의 시간복잡도

     :  최악의 경우에도 Θ(E log V) 

        * 이때 E는 간선의 개수, V는 노드의 개수

 

  Heap 이용  :  보러가기

 

 

🐸 실행 과정

✔ 처음에는 1차원 배열을 선언.

✔ 단계마다 '방문하지 않는노드 중, 최단 거리가 가장 짧은 노드를 선택'하기 위해 단계마다 1차원 리스트를 순차 탐색(원소를 모두 확인)

 

✔ ✔ 현재 가장 가까운 노드를 저장하기 위한 목적으로만 우선순위 큐를 추가 이용합니다.

 

✔ 아래의 방법을 반복합니다.

 
+) 우선순위 큐 (거리 : 0, 노드 : 1)

 +) 우선순위 큐 (거리 : 1, 노드 : 4) (거리 : 2, 노드 : 2) (거리 : 5, 노드 : 3)

**거리가 짧은 노드가 우선 순위 큐의 최상위 원소가 된다.

 

 

💻 코드 예시

     ** 전체 노드의 개수가 5,000개 이하일 때,

     **** getSmallNode( )함수를 구현할 필요가 없다. 

              최단 거리의 노드를 선택할 때, 우선 순위 큐를 이용하면 되기 때문.

     ***** C++의 priority_queue의 경우 "최대 힙"으로 우선 순위가 구현되어 있기 때문에 최소 힙으로 구현하기 위해서는 -를 붙여준다.

int dist = -pq.top().first;
pq.push(make_pair(-cost, graph[now][i].fisrt));
#include <iostream>
#include <vector>
#include <priority_queue>
using namespace std;

/*
* 코드 참고 : https://github.com/ndb796/python-for-coding-test/blob/master/9/2.cpp
*/

int n, m, start;
vector<pair<int, int>> graph[100001];

int d[100001]; // 최단 거리 테이블

void dijkstra(int start){
	priority_queue<pair<int, int>> pq;
	
	// 시작 노드로 가기 위한 최단 경로는 0으로 설정하여, 큐에 삽입
	pq.push({0, start});
	d[start] = 0;
	
	while(!pq.empty()){
		// 가장 최단 거리 짧은 노드 꺼내기
		int dist = -pq.top().first; // 현재 노드의 비용
		int now = pq.top().second; // 현재 노드
		pq.pop();
		
		if(d[now] < dist) continue; // 현재 노드가 이미 처리된 적이 있는 노드라면 무시
		
		for(int i = 0; i < graph[now].size(); i++){
			int cost = dist + graph[now][i].second();
			
			// 현재 노드를 거쳐서, 다른 노드로 이동하는 거리가 더 짧은 경우
			if(cost < d[graph[now][j].first]){
				d[graph[now][i].first] = cost;
				pq.push(make_pair(-cost, graph[now][i].fisrt));
			}
		}
	}
}

int main() {
	
	cin >> n >> m >> start;
	
	// 모든 간선 정보를 입력받기
	for(int i = 0; i < m; i++){
		int a, b, c;
		cin a >> b >> c;
		// a번 노드에서 b번 노드로 가는 비용이 c라는 의미
		graph[a].push_back(make_pair(b, c));
	}
	
	d[100001] = {1e9,};
	
	dijkstra(start);
	
	for(int i = 1; i <= n; i++){
		if(d[i] != 1e9) cout << d[i] << " ";
	}
	
	return 0;
}

 


🏇 플로이드 워셜 알고리즘

         "모든 지점에서 다른 모든 지점까지이 최단 경로를 모두 구해야 하는 경우 사용하는 알고리즘"

 

✔ 단계마다 '거쳐가는 노드 기준'으로 알고리즘 수행

    BUT 매번 방문 X 노드는 찾을 필요가 없다.

✔  2차원 리스트에 최단 거리 정보 저장

✔ 노드가 N개일 때, N번만큼 단계를 반복하며 '점화식에 맞게' 2차원 리스트를 갱신하기 때문에 'DP'로 분류됩니다.

 

✔   3중 반복문으로 점화식에 따라 2차원 리스트를 갱신하면 됩니다.

 

 

  ⏱ 시간복잡도

     :  Θ(N^3) 

 

 

  🐸 실행 과정

 

 

💻 코드 예시

#include <iostream>
#include <vector>
using namespace std;

// 노드 개수는 최대 500개라고 가정한다.

// 노드 개수 n, 간선 개수 m
int n, m;

int graph[501][501]; // 최단 거리 테이블

int main() {
	cin >> n >> m;
	
	// 최단 거리 테이블 무한으로 초기화
	for(int i = 0; i < 501; i++){
		for(int j = 0; j < 501; j++){
			graph[i][j] = 1e9;
		}
	}
	
	// 자기 자신에서 자기 자신으로 가는 거리는 0
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= n; j++){
			if(i == j) graph[i][j] = 0;
		}
	}
	
	// 각 간선 정보 입력 받기
	for(int i = 0; i < m; i++){
		int a, b, c; // A에서 B로 가는 비용은 C라고 설정
		cin >> a >> b >> c;
		graph[a][b] = c;
	}
	
	// 점화식에 따라 플로이드 워셜 알고리즘 수행
	for(int k = 1; k <= n; k++){
		for(int i = 1; i <= n; i++){
			for(int j = 1; j <= n; j++){
				graph[i][j] = min(graph[i][j], graph[i][k] + graph[k][j]);
			}
		}
	}
	
	
	//결과 출력
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= n; j++){
			if(graph[i][j] != ) cout << graph[i][j];
		}
		cout << endl;
	}
	
	return 0;
}

// 코드 참고 : https://github.com/ndb796/python-for-coding-test/blob/master/9/3.cpp

 

 

 

🌷 문제

    이것이 취업을 위한 코딩 테스트다

       유형 1 - 미래 도시

       유형 2 - 전보

 

 

 


 

 

'OLD_알고리즘' 카테고리의 다른 글

Solving Skill ] 완전 탐색  (0) 2021.04.16
완탐中 ] 순열과 조합  (0) 2021.04.16
자료구조 ] Heap 자료구조  (0) 2021.03.22
Solving Skill ] 다이나믹 프로그래밍(DP)  (0) 2021.03.16
Solving Skill ] 이진 탐색  (0) 2021.03.10

댓글