text: 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: weight });
          this.adjacencyList[vertex2].push({ node: vertex1, weight: weight });
      }
  
      //Dijkstra's shortest path
      dijkstra(start, finish) {
          const nodes = new PriorityQueue();
          const distances = {};
          const previous = {};
          let path = []; //to return at the end
          let smallest;
  
          //build up initial state - looping over adjacency list
          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;
          }
          //loop as long as there is something to visit
          while (nodes.values.length) {
              smallest = nodes.dequeue().val;
              if (smallest === finish) {
                  //Done, need to build path to return
                  while (previous[smallest]) {
                      path.push(smallest);
                      smallest = previous[smallest];
                  }
                  break;
              }
              if (smallest || distances[smallest] !== Infinity) {
                  for (let neighbor in this.adjacencyList[smallest]) {
                      //find neighboring node
                      let nextNode = this.adjacencyList[smallest][neighbor];
                      //calculate new distance to neighboring node
                      let candidate = distances[smallest] + nextNode.weight;
                      let nextNeighbor = nextNode.node;
                      if (candidate < distances[nextNeighbor]) {
                          //updating new smallest distance to neighbor
                          distances[nextNeighbor] = candidate;
                          //updating previous - how we got to neighbor
                          previous[nextNeighbor] = smallest;
                          //enqueue in priority queue with new priority
                          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 index = this.values.length - 1;
          const element = this.values[index];
          while (index > 0) {
              let parentIndex = Math.floor((index - 1) / 2);
              let parent = this.values[parentIndex];
              if (element.priority >= parent.priority) break;
              this.values[parentIndex] = element;
              this.values[index] = parent;
              index = parentIndex;
          }
      }
  
      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 index = 0;
          const length = this.values.length;
          const element = this.values[0];
          while (true) {
              let leftChildIdx = (2 * index) + 1;
              let rightChildIdx = (2 * index) + 2;
              let leftChild, 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[index] = this.values[swap];
              this.values[swap] = element;
              index = swap;
          }
      }
  }
  
  class Node {
      constructor(val, priority){
          this.val = val;
          this.priority = priority;
      }
  }
  
  let g = new WeightedGraph();
  g.addVertex('A');
  g.addVertex('B');
  g.addVertex('C');
  g.addVertex('D');
  g.addVertex('E');
  g.addVertex('F');
  g.addEdge('A', 'B', 4);
  g.addEdge('A', 'C', 2);
  g.addEdge('B', 'E', 3);
  g.addEdge('C', 'D', 2);
  g.addEdge('C', 'F', 4);
  g.addEdge('D', 'E', 3);
  g.addEdge('D', 'F', 1);
  g.addEdge('E', 'F', 1);
  
  console.log(g.dijkstra("A", "E"));