알고리즘/개념

[알고리즘] 다익스트라 알고리즘(Dijkstra Algorithm)

넌뭐가그렇게중요해 2025. 4. 1. 11:32

다익스트라 알고리즘(Dijkstra Algorithm)

다익스트라 알고리즘은 가중치가 있는 그래프에서 한 정점(출발점)으로부터 다른 모든 정점까지의 최단 경로를 찾는 알고리즘입니다. 

  • 필수 조건 : 모든 간선의 가중치는 0 이상의 값이어야 하며, 음수 가중치가 존재하면 올바른 결과를 보장할 수 없습니다. → 음수 가중치는 나중에 배울 벨만-포드 알고리즘을 사용합니다.
  • 분류: 그리디 접근 방식으로 매 단계마다 "현재까지의 최단 경로"를 이용하여 다음 정점을 선택합니다.

동작 과정 및 의사 코드

1. 초기화

  • 모든 노드까지의 최단 거리를 무한대(INF)로 설정 → 최단 거리기 때문에 초기 값을 무한대로 설정
  • 시작 노드의 최단 거리는 0으로 설정
  • 우선순위 큐(힙)에 (거리, 노드) 형태로 시작 노드를 추가 → (거리, 노드) / (노드, 거리) 상관 X 단) 거리 기준으로 우선순위를 부여

2. 우선순위 큐(힙)에서 노드 탐색

  • 큐가 빌때까지 반복
  • 현재 최단 거리로 도달 가능한 노드를 큐에서 꺼냄
  • 이미 더 짧은 경로가 존재하는 경우를 무시

3. 현재 노드와 연결된 모든 인접노드 탐색

  • 인접한 노드들 하나씩 확인
  • 현재 노드를 거쳐 가는 새로운 거리(비용)를 계산
  • 만약 새로운 거리가 기준보다 짧다면 갱신
  • 갱신된 노드를 우선순위 큐에 추가

4. 모든 노드를 처리하면 종료

  • 최단 거리를 담은 리스트에 모든 노드의 최소 비용이 저장됨

의사 코드

import heapq

def dijkstra(n, start, graph):
    """
    다익스트라 알고리즘을 통해 시작 노드로부터 다른 모든 노드까지의 최단 경로를 구하는 함수.
    
    매개변수:
        n (int): 노드의 총 개수 (노드 번호는 1부터 n까지)
        start (int): 시작 노드 번호
        graph (list): 인접 리스트 형태의 그래프. 각 인덱스 i는 (노드, 가중치) 튜플 리스트를 포함.
        
    반환값:
        distance (list): 시작 노드로부터 각 노드까지의 최소 비용 리스트.
    """
    
    INF = int(1e9)  # 무한대를 의미하는 값 (여기서는 10억 사용)
    # 최단 거리 테이블을 무한대로 초기화 (1번부터 n번 노드까지)
    distance = [INF] * (n + 1)
    distance[start] = 0  # 시작 노드까지의 거리는 0으로 설정
    
    # 우선순위 큐(최소 힙) 초기화 및 시작 노드 추가
    q = []
    heapq.heappush(q, (0, start))  # (현재까지의 비용, 노드)
    
    # 큐가 빌 때까지 반복하면서 최단 경로 탐색
    while q:
        # 가장 비용이 적은 노드 (currentDistance, currentNode)를 큐에서 꺼냄
        dist, now = heapq.heappop(q)
        
        # 이미 더 짧은 경로가 기록되어 있다면 무시
        if dist > distance[now]:
            continue
        
        # 현재 노드(now)에 연결된 모든 인접 노드(i) 탐색
        for i in graph[now]:
            # i[0]: 인접 노드, i[1]: 현재 노드에서 인접 노드로 가는 비용
            cost = dist + i[1]  # 현재 노드를 거쳐 인접 노드로 가는 새로운 비용 계산
            
            # 만약 새로운 비용이 기존의 최단 경로보다 짧다면
            if cost < distance[i[0]]:
                # 최단 거리 갱신
                distance[i[0]] = cost
                # 갱신된 인접 노드를 우선순위 큐에 추가하여 이후에 처리
                heapq.heappush(q, (cost, i[0]))
                
    return distance

# 예시 사용법:
if __name__ == "__main__":
    # 노드 수와 간선 수를 정의 (예시: 5개의 노드)
    n = 5  
    start = 1  # 시작 노드 설정
    # 그래프 초기화 (1번부터 5번 노드까지)
    graph = [[] for _ in range(n + 1)]
    
    # 예시: 간선 추가 (a, b, c: a 노드에서 b 노드로 가는 비용 c)
    # 아래는 임의의 간선 예시입니다.
    graph[1].append((2, 2))
    graph[1].append((3, 5))
    graph[2].append((3, 1))
    graph[2].append((4, 2))
    graph[3].append((4, 3))
    graph[4].append((5, 1))
    
    # 다익스트라 알고리즘 실행
    shortest_distances = dijkstra(n, start, graph)
    
    # 결과 출력: 각 노드까지의 최단 거리
    for i in range(1, n + 1):
        print(f"노드 {start}에서 노드 {i}까지의 최단 거리: {shortest_distances[i]}")

장단점

장점

  • 구현이 간단 → 아직 미숙해서 어렵나 봐요....
    개념이 직관적이며, 기본적인 자료구조(배열, 우선순위 큐 등)만 있으면 쉽게(?????) 구현할 수 있습니다. 
  • 정확한 최단 경로 산출
    음수 가중치가 없는 그래프에서 항상 정확한 최단 경로를 보장합니다. 
  • 실제 경로 추적 가능
    최단 거리뿐만 아니라, 경유한 노드들을 기록하여 실제 경로를 재구성할 수 있습니다. 

단점

  • 음수 가중치에 취약
    그래프의 음의 가중치가 존재할 경우 올바른 결과를 보장하지 않습니다. 
  • 대규모 그래프의 경우 성능 문제
    정점(Vertex)과 간선(Edge)의 수가 매우 많을 경우, 우선순위 큐 사용 시에도 계산 비용이 증가할 수 있습니다. 
  • 동적 그래프에 부적합
    노드나 간선이 자주 변경되는 환경에서는 매번 저체 알고리즘을 재실행해야 하므로 비효율적입니다. 

실생활에서의 다익스트라

1. GPS 내비게이션 시스템

  • 설명: 차량의 출발지에서 목적지까지 최적의 경로를 찾기 위해 사용됩니다.
  • 실제 활용: 지도상의 도로 거리와 교통 상황 등을 반영하여 운전자에게 빠르고 효율적인 경로를 안내합니다.

2. 네트워크 라우팅

  • 설명: 데이터 패킷이 네트워크를 통해 최종 목적지에 도달하는 최적의 경로를 결정합니다.
  • 실제 활용: OSPF(Open Shortest Path First)와 같은 라우팅 프로토콜은 네트워크 상의 노드들 간 최단 경로를 산출하여 데이터 전송 효율을 극대화합니다.

3. 물류 및 배송 최적화

  • 설명: 여러 창고나 배송지 간의 이동 경로를 최적화하여 비용과 시간을 절감합니다.
  • 실제 활용: 배송 경로를 최적화함으로써 연료 비용을 줄이고, 배송 시간을 단축하는 데 기여합니다.