ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 알고리즘, graph, Dijkstra
    Data Structure & Algorithm 2024. 7. 3. 18:18

     

    다익스트라 알고리즘(Dijkstra's Algorithm)은 그래프에서 한 정점에서 다른 모든 정점으로의 최단 경로를 찾는 데 사용되는 알고리즘입니다. 이 알고리즘은 가중치가 있는 그래프에서 사용되며, 가중치가 음수가 아닌 경우에만 적용할 수 있습니다.

    다익스트라 알고리즘의 개념

    1. 시작 정점 선택: 알고리즘은 시작 정점에서 시작합니다.
    2. 최단 경로 집합: 최단 경로를 확정한 정점들의 집합을 유지합니다.
    3. 미확정 집합: 아직 최단 경로가 확정되지 않은 정점들의 집합을 유지합니다.
    4. 거리 배열: 시작 정점에서 각 정점까지의 현재까지 발견된 최단 거리를 저장합니다. 초기에는 시작 정점의 거리를 0으로 설정하고 나머지 정점의 거리를 무한대로 설정합니다.
    5. 이웃 정점 업데이트: 현재 정점에서 인접한 정점들로의 거리를 계산하고, 더 짧은 경로를 발견하면 거리를 업데이트합니다.
    6. 반복: 모든 정점의 최단 경로가 확정될 때까지 이 과정을 반복합니다.

    다익스트라 알고리즘의 과정

    출처 [https://cs.slides.com/colt_steele/graphs#/115]

     

     

    1. 시작 정점을 설정하고, 시작 정점의 거리를 0으로 설정합니다.
    2. 현재 정점에서 인접한 모든 정점의 거리를 업데이트합니다.
    3. 아직 방문하지 않은 정점 중 가장 거리가 짧은 정점을 선택하여, 그 정점으로 이동합니다.
    4. 이 과정을 모든 정점이 방문될 때까지 반복합니다.

    다익트라 메서드의 의사코드

    • 이 함수는 시작 정점과 끝 정점을 받아야 합니다.
    • 객체(이를 distances라고 부르겠습니다)를 만들고 각 키를 인접 리스트의 모든 정점으로 설정하되, 시작 정점은 0의 값을 가지도록 하고 나머지는 무한대의 값을 가지도록 합니다.
    • distances 객체에 값을 설정한 후, 시작 정점을 제외한 각 정점을 우선순위 큐에 무한대의 우선순위로 추가합니다. 시작 정점은 우리가 시작하는 곳이므로 우선순위가 0이어야 합니다.
    • previous라는 또 다른 객체를 만들고 각 키를 인접 리스트의 모든 정점으로 설정하되, 값은 null로 설정합니다.
    • 우선순위 큐에 항목이 있는 동안 반복을 시작합니다.
      • 우선순위 큐에서 정점을 dequeue합니다.
      • 해당 정점이 끝 정점과 같으면 - 완료입니다!
      • 그렇지 않으면 해당 정점의 인접 리스트의 각 값을 반복합니다.
        • 시작 정점에서 해당 정점까지의 거리를 계산합니다.
        • 그 거리가 현재 distances 객체에 저장된 값보다 작으면
          • distances 객체를 새로운 더 짧은 거리로 업데이트합니다.
          • previous 객체에 해당 정점을 포함하도록 업데이트합니다.
          • 시작 노드로부터의 총 거리로 정점을 큐에 enqueue합니다.

    자바스크립트로 다익스트라 알고리즘 구현

    class PriorityQueue {
      constructor() {
        this.values = [];
      }
      enqueue(val, priority) {
        this.values.push({ val, priority });
        this.sort();
      }
      dequeue() {
        return this.values.shift();
      }
      sort() {
        this.values.sort((a, b) => a.priority - b.priority);
      }
    }
    
    class WeightedGraph {
      constructor() {
        this.adjacencyList = {};
      }
      addVertex(vertex) {
        if (!this.adjacencyList[vertex]) this.adjacencyList[vertex] = [];
      }
      addEdge(vertex1, vertex2, weight) {
        this.adjacencyList[vertex1].push({ node: vertex2, weight });
        this.adjacencyList[vertex2].push({ node: vertex1, weight });
      }
      Dijkstra(start, finish) {
        // nodes dequeue 할 때, 가중치가 낮은 것을 먼저 뽑아 순회하기 위한 우선순위 큐 클래스
        const nodes = new PriorityQueue();
        // distances - 시작 정점을 기준으로 가장 짧은 거리를 저장해두는 객체
        const distances = {};
        // previous - 최종적으로 최단거리를 기억할 수 있는 객체
        const previous = {};
        // target - 우리가 실제로 방문하는 노드
        let target;
        // path - 최단 거리를 저장해서 반환할 배열
        let path = [];
    
        //build up initial state
        for (let vertex in this.adjacencyList) {
          if (vertex === start) {
            distances[vertex] = 0;
            nodes.enqueue(vertex, 0);
          } else {
            distances[vertex] = Infinity;
            nodes.enqueue(vertex, Infinity);
          }
          previous[vertex] = null;
        }
    
        console.log('nodes.values:', nodes.values);
        console.log('distances:', distances);
        console.log('previous:', previous);
        console.log('adjacencyList:', this.adjacencyList);
        // as long as there is something to visit
        while (nodes.values.length) {
          target = nodes.dequeue().val;
          if (target === finish) {
            //WE ARE DONE
            //BUILD UP PATH TO RETURN AT END
            while (previous[target]) {
              path.push(target);
              target = previous[target];
            }
            break;
          }
          if (target || distances[target] !== Infinity) {
            for (let neighbor in this.adjacencyList[target]) {
              //find neighboring node
              let nextNode = this.adjacencyList[target][neighbor];
              //calculate new distance to neighboring node
              let newDistance = distances[target] + nextNode.weight;
    
              console.log('nextNode:', nextNode);
              console.log('newDistance:', newDistance);
    
              if (newDistance < distances[nextNode.node]) {
                //updating new target distance to neighbor
                distances[nextNode.node] = newDistance;
                //updating previous - How we got to neighbor
                previous[nextNode.node] = target;
                //enqueue in priority queue with new priority
                nodes.enqueue(nextNode.node, newDistance);
              }
            }
          }
        }
        return path.concat(target).reverse();
      }
    }
    
    var graph = new WeightedGraph();
    graph.addVertex('A');
    graph.addVertex('B');
    graph.addVertex('C');
    graph.addVertex('D');
    graph.addVertex('E');
    graph.addVertex('F');
    
    graph.addEdge('A', 'B', 4);
    graph.addEdge('A', 'C', 2);
    graph.addEdge('B', 'E', 3);
    graph.addEdge('C', 'D', 2);
    graph.addEdge('C', 'F', 4);
    graph.addEdge('D', 'E', 3);
    graph.addEdge('D', 'F', 1);
    graph.addEdge('E', 'F', 1);
    
    console.log(graph.Dijkstra('A', 'E'));
    
    // ["A", "C", "D", "F", "E"]

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

    • 우선순위 큐를 사용하지 않은 경우: (O(V^2))
    • 우선순위 큐를 사용한 경우 (예: 힙 사용):
      • 삽입/삭제: (O(log V))
      • 총 시간 복잡도: (O((V + E) log V))

    다익스트라 알고리즘의 특징

    1. 단일 시작점: 시작 정점에서 다른 모든 정점으로의 최단 경로를 구합니다.
    2. 비음수 가중치: 간선의 가중치가 음수인 경우에는 사용할 수 없습니다.
    3. 효율성: 우선순위 큐(힙)를 사용하면 큰 그래프에서도 비교적 효율적으로 작동합니다.

    다익스트라 알고리즘은 네트워크 라우팅, 지도 서비스, 교통 시스템 등 다양한 분야에서 널리 사용됩니다.

    댓글

Designed by Tistory.