Computer >> 컴퓨터 >  >> 프로그램 작성 >> JavaScript

Javascript의 Prim 알고리즘


Prim의 알고리즘은 가중된 무방향 그래프에 대한 최소 스패닝 트리를 찾는 욕심 많은 알고리즘입니다. 모든 정점을 포함하는 트리를 형성하는 가장자리의 하위 집합을 찾습니다. 여기서 트리의 모든 가장자리의 총 가중치는 최소화됩니다.

이 알고리즘은 각 단계에서 임의의 시작 정점에서 한 번에 하나의 정점을 트리에서 다른 정점으로 가장 저렴한 연결을 추가하여 이 트리를 구축하는 방식으로 작동합니다.

Prim의 알고리즘은 어떻게 작동합니까?

Prim의 알고리즘이 작동하는 방식에 대한 그림을 살펴보겠습니다. -

1. 임의의 노드를 루트 노드로 선택합니다. 이 경우 S 노드를 Prim의 스패닝 트리의 루트 노드로 선택합니다. 이 노드는 임의로 선택되므로 모든 노드가 루트 노드가 될 수 있습니다. 모든 비디오가 루트 노드가 될 수 있는 이유가 궁금할 수 있습니다. 따라서 답은 스패닝 트리에 그래프의 모든 노드가 포함되어 있고 연결되어 있기 때문에 트리의 나머지 부분에 연결하는 가장자리가 최소한 하나는 있어야 한다는 것입니다.

2. 나가는 간선을 확인하고 비용이 덜 드는 간선을 선택합니다. 루트 노드 S를 선택한 후 S, A, S, C가 각각 가중치가 7과 8인 두 개의 간선임을 알 수 있습니다. 우리는 가장자리 S, A가 다른 것보다 작기 때문에 선택합니다.

Javascript의 Prim 알고리즘

이제 트리 S-7-A는 하나의 노드로 취급되며 여기서 나가는 모든 가장자리를 확인합니다. 비용이 가장 저렴한 것을 선택하여 트리에 포함시킵니다.

Javascript의 Prim 알고리즘

이 단계 후에 S-7-A-3-C 트리가 형성됩니다. 이제 다시 노드로 처리하고 모든 가장자리를 다시 확인합니다. 그러나 우리는 가장 비용이 적게 드는 가장자리만 선택할 것입니다. 이 경우 C-3-D는 다른 가장자리의 비용 8, 6, 4 등보다 적은 새 가장자리입니다.

Javascript의 Prim 알고리즘

노드 D를 추가한 후 스패닝 트리에 대해 이제 동일한 비용을 갖는 두 개의 모서리, 즉 D-2-T 및 D-2-B가 있습니다. 따라서 둘 중 하나를 추가할 수 있습니다. 그러나 다음 단계는 다시 가장자리 2를 최소 비용으로 산출합니다. 따라서 우리는 양쪽 가장자리가 포함된 스패닝 트리를 보여주고 있습니다.

Javascript의 Prim 알고리즘

이제 코드에서 동일하게 구현하는 방법을 살펴보겠습니다.

예시

primsMST() {
   // Initialize graph that'll contain the MST
   const MST = new Graph();
   if (this.nodes.length === 0) {
      return MST;
   }


   // Select first node as starting node
   let s = this.nodes[0];


   // Create a Priority Queue and explored set
   let edgeQueue = new PriorityQueue(this.nodes.length * this.nodes.length);
   let explored = new Set();
   explored.add(s);
   MST.addNode(s);


   // Add all edges from this starting node to the PQ taking weights as priority
   this.edges[s].forEach(edge => {
      edgeQueue.enqueue([s, edge.node], edge.weight);
   });


   // Take the smallest edge and add that to the new graph
   let currentMinEdge = edgeQueue.dequeue();
   while (!edgeQueue.isEmpty()) {


      // COntinue removing edges till we get an edge with an unexplored node
      while (!edgeQueue.isEmpty() && explored.has(currentMinEdge.data[1])) {
         currentMinEdge = edgeQueue.dequeue();
      }
      let nextNode = currentMinEdge.data[1];


      // Check again as queue might get empty without giving back unexplored element
      if (!explored.has(nextNode)) {
         MST.addNode(nextNode);
         MST.addEdge(currentMinEdge.data[0], nextNode, currentMinEdge.priority);
         // Again add all edges to the PQ
         this.edges[nextNode].forEach(edge => {
            edgeQueue.enqueue([nextNode, edge.node], edge.weight);
         });


         // Mark this node as explored explored.add(nextNode);
         s = nextNode;
      }
   }
   return MST;
}

다음을 사용하여 테스트할 수 있습니다.

let g = new Graph();
g.addNode("A");
g.addNode("B");
g.addNode("C");
g.addNode("D");
g.addNode("E");
g.addNode("F");
g.addNode("G");


g.addEdge("A", "C", 100);
g.addEdge("A", "B", 3);
g.addEdge("A", "D", 4);
g.addEdge("C", "D", 3);
g.addEdge("D", "E", 8);
g.addEdge("E", "F", 10);
g.addEdge("B", "G", 9);
g.primsMST().display();

출력

이것은 출력을 줄 것입니다 -

A->B, D
B->A, G
D->A, C, E
C->D
E->D, F
G->B
F->E

초기 그래프는 -

예시

/**
   *         A
   *       / | \
   *       C | B
   *       \ | |
   *         D G
   *         | /
   *         E
   *         |
   *         F
*/

출력

현재 그래프는 다음과 같습니다. -

/**
   *         A
   *         |\
   *     C   | B
   *      \  | |
   *       D   G
   *       |
   *       E
   *       |
   *       F
   *
*/

가장 값비싼 모서리를 제거하고 이제 스패닝 트리를 갖게 되었습니다.