본문 바로가기
자료구조

[JS] 다익스트라 알고리즘

by 아촌 2022. 12. 24.

다익스트라 알고리즘은 도로 교통망 같은 곳에서 나타날 수 있는 그래프에서 꼭짓점 간의 최단 경로를 찾는 알고리즘이다. 이 알고리즘은 컴퓨터 과학자 에츠허르 데이크스트라가 1956년에 고안했으며 삼 년 뒤에 발표했다

 

https://youtu.be/tZu4x5825 LI

 

다익스트라 알고리즘은 '그래프'와 '우선순위 큐(이진힙)' 개념을 이해하고 있어야 하며

두 정점 간의 최단 거리를 찾는 알고리즘이다.

 

 

//가중그래프
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) {
    const nodes = new PriorityQueue();
    	//distances 객체는 시작점부터 그 노드까지 최단거리를 기록
    const distances = {};
    	// previous 객체는 지나온 노드를 기록
    const previous = {};
    let path = [];
    let smallest;

    // 초기 상태를 루프로 만들어준다
    // distances { A: 0, B: Infinity, C: Infinity, D: Infinity, E: Infinity, F: Infinity}
    // previous { A: null, B: null, C: null, D: null, E: null, F: null }
    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;
    }
    
    //무한 루프를 돌린다.
    while (nodes.values.length) {
    //dequeue로 우선순위가 가장 높은값(가장 최단거리)를 smallest변수에 재할당
      smallest = nodes.dequeue().val;
      //smallest 가 목표하는노드와 같으면
      if (smallest === finish) {
        // return할 값을 아래의 while문을 통해 만들고 상위 while문을 break한다. 
        while (previous[smallest]) {
          path.push(smallest);
          smallest = previous[smallest];
        }
        break;
      }
      //dequeue값이 finish와 같지 않으면
      if (smallest || distances[smallest] !== Infinity) {
        for (let neighbor in this.adjacencyList[smallest]) {
          //이웃노드 찾음
          let nextNode = this.adjacencyList[smallest][neighbor];
          let candidate = distances[smallest] + nextNode.weight;
          let nextNeighbor = nextNode.node;

          if (candidate < distances[nextNeighbor]) {
            //updating new smallest distance to neighbor
            distances[nextNeighbor] = candidate;
            // previous 업데이트
            previous[nextNeighbor] = smallest;
            // 엔큐
            nodes.enqueue(nextNeighbor, candidate);
          }
        }
      }
    }
    return path.concat(smallest).reverse();
  }
}

class PriorityQueue {
  constructor() {
    this.values = [];
  }

  enqueue(val, priority) {
    let newNode = new Node(val, priority);
    this.values.push(newNode);
    this.bubbleUp();
  }

  bubbleUp() {
    let idx = this.values.length - 1;
    const element = this.values[idx];
    while (idx > 0) {
      let parentIdx = Math.floor((idx - 1) / 2);
      let parent = this.values[parentIdx];
      if (element.priority >= parent.priority) break;
      this.values[parentIdx] = element;
      this.values[idx] = parent;
      idx = parentIdx;
    }
  }

  dequeue() {
    const min = this.values[0];
    const end = this.values.pop();
    if (this.values.length > 0) {
      this.values[0] = end;
      this.sinkDown();
    }
    return min;
  }
  sinkDown() {
    let idx = 0;
    const length = this.values.length;
    const element = this.values[0];
    while (true) {
      let leftChildIdx = 2 * idx + 1;
      let rightChildIdx = 2 * idx + 2;
      let leftChild;
      let rightChild;
      let swap = null;

      if (leftChildIdx < length) {
        leftChild = this.values[leftChildIdx];
        if (leftChild.priority < element.priority) {
          swap = leftChildIdx;
        }
      }
      if (rightChildIdx < length) {
        rightChild = this.values[rightChildIdx];
        if (
          (swap === null && rightChild.priority < element.priority) ||
          (swap !== null && rightChild.priority < leftChild.priority)
        ) {
          swap = rightChildIdx;
        }
      }
      if (swap === null) break;
      this.values[idx] = this.values[swap];
      this.values[swap] = element;
      idx = swap;
    }
  }
}
class Node {
  constructor(val, priority) {
    this.val = val;
    this.priority = priority;
  }
}

let 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'));
반복문에서   마다 distances 객체에 기록된 

{ A: 0, B: Infinity, C: Infinity, D: Infinity, E: Infinity, F: Infinity}
{ A: 0, B: 4, C: 2, D: Infinity, E: Infinity, F: Infinity }
{ A: 0, B: 4, C: 2, D: 4, E: Infinity, F: 6 }
{ A: 0, B: 4, C: 2, D: 4, E: 7, F: 6 }
{ A: 0, B: 4, C: 2, D: 4, E: 7, F: 5 }
{ A: 0, B: 4, C: 2, D: 4, E: 6, F: 5 }
{ A: 0, B: 4, C: 2, D: 4, E: 6, F: 5 }
반복문에서   마다 previous 객체에 기록된 

{ A: null, B: null, C: null, D: null, E: null, F: null }
{ A: null, B: 'A', C: 'A', D: null, E: null, F: null }
{ A: null, B: 'A', C: 'A', D: 'C', E: null, F: 'C' }
{ A: null, B: 'A', C: 'A', D: 'C', E: 'B', F: 'C' }
{ A: null, B: 'A', C: 'A', D: 'C', E: 'B', F: 'D' }
{ A: null, B: 'A', C: 'A', D: 'C', E: 'F', F: 'D' }
{ A: null, B: 'A', C: 'A', D: 'C', E: 'F', F: 'D' }

'자료구조' 카테고리의 다른 글

[JS] 그래프 순회  (0) 2022.12.24
[JS] 그래프  (0) 2022.12.23
[JS] 해시테이블  (1) 2022.12.22
[JS] 이진 힙  (0) 2022.12.21
[JS] 트리 순회  (0) 2022.12.18