Add Prim.

This commit is contained in:
Oleksii Trekhleb
2018-05-07 12:45:10 +03:00
parent 50df3bf717
commit cad8ccd9bb
9 changed files with 194 additions and 2 deletions

View File

@ -44,3 +44,4 @@ are two possibilities of minimum spanning tree of the given graph.
- [Minimum Spanning Tree on Wikipedia](https://en.wikipedia.org/wiki/Minimum_spanning_tree)
- [Prim's Algorithm on Wikipedia](https://en.wikipedia.org/wiki/Prim%27s_algorithm)
- [Prim's Algorithm on YouTube by Tushar Roy](https://www.youtube.com/watch?v=oP2-8ysT3QQ)
- [Prim's Algorithm on YouTube by Michael Sambol](https://www.youtube.com/watch?v=cplfcGZmX7I)

View File

@ -0,0 +1,91 @@
import GraphVertex from '../../../../data-structures/graph/GraphVertex';
import GraphEdge from '../../../../data-structures/graph/GraphEdge';
import Graph from '../../../../data-structures/graph/Graph';
import prim from '../prim';
describe('prim', () => {
it('should fire an error for directed graph', () => {
function applyPrimToDirectedGraph() {
const graph = new Graph(true);
prim(graph);
}
expect(applyPrimToDirectedGraph).toThrowError();
});
it('should find minimum spanning tree', () => {
const vertexA = new GraphVertex('A');
const vertexB = new GraphVertex('B');
const vertexC = new GraphVertex('C');
const vertexD = new GraphVertex('D');
const vertexE = new GraphVertex('E');
const vertexF = new GraphVertex('F');
const vertexG = new GraphVertex('G');
const edgeAB = new GraphEdge(vertexA, vertexB, 2);
const edgeAD = new GraphEdge(vertexA, vertexD, 3);
const edgeAC = new GraphEdge(vertexA, vertexC, 3);
const edgeBC = new GraphEdge(vertexB, vertexC, 4);
const edgeBE = new GraphEdge(vertexB, vertexE, 3);
const edgeDF = new GraphEdge(vertexD, vertexF, 7);
const edgeEC = new GraphEdge(vertexE, vertexC, 1);
const edgeEF = new GraphEdge(vertexE, vertexF, 8);
const edgeFG = new GraphEdge(vertexF, vertexG, 9);
const edgeFC = new GraphEdge(vertexF, vertexC, 6);
const graph = new Graph();
graph
.addEdge(edgeAB)
.addEdge(edgeAD)
.addEdge(edgeAC)
.addEdge(edgeBC)
.addEdge(edgeBE)
.addEdge(edgeDF)
.addEdge(edgeEC)
.addEdge(edgeEF)
.addEdge(edgeFC)
.addEdge(edgeFG);
expect(graph.getWeight()).toEqual(46);
const minimumSpanningTree = prim(graph);
expect(minimumSpanningTree.getWeight()).toBe(24);
expect(minimumSpanningTree.getAllVertices().length).toBe(graph.getAllVertices().length);
expect(minimumSpanningTree.getAllEdges().length).toBe(graph.getAllVertices().length - 1);
expect(minimumSpanningTree.toString()).toBe('A,B,D,C,E,F,G');
});
it('should find minimum spanning tree for simple graph', () => {
const vertexA = new GraphVertex('A');
const vertexB = new GraphVertex('B');
const vertexC = new GraphVertex('C');
const vertexD = new GraphVertex('D');
const edgeAB = new GraphEdge(vertexA, vertexB, 1);
const edgeAD = new GraphEdge(vertexA, vertexD, 3);
const edgeBC = new GraphEdge(vertexB, vertexC, 1);
const edgeBD = new GraphEdge(vertexB, vertexD, 3);
const edgeCD = new GraphEdge(vertexC, vertexD, 1);
const graph = new Graph();
graph
.addEdge(edgeAB)
.addEdge(edgeAD)
.addEdge(edgeBC)
.addEdge(edgeBD)
.addEdge(edgeCD);
expect(graph.getWeight()).toEqual(9);
const minimumSpanningTree = prim(graph);
expect(minimumSpanningTree.getWeight()).toBe(3);
expect(minimumSpanningTree.getAllVertices().length).toBe(graph.getAllVertices().length);
expect(minimumSpanningTree.getAllEdges().length).toBe(graph.getAllVertices().length - 1);
expect(minimumSpanningTree.toString()).toBe('A,B,C,D');
});
});

View File

@ -0,0 +1,73 @@
import Graph from '../../../data-structures/graph/Graph';
import PriorityQueue from '../../../data-structures/priority-queue/PriorityQueue';
/**
* @param {Graph} graph
* @return {Graph}
*/
export default function prim(graph) {
// It should fire error if graph is directed since the algorithm works only
// for undirected graphs.
if (graph.isDirected) {
throw new Error('Prim\'s algorithms works only for undirected graphs');
}
// Init new graph that will contain minimum spanning tree of original graph.
const minimumSpanningTree = new Graph();
// This priority queue will contain all the edges that are starting from
// visited nodes and they will be ranked by edge weight - so that on each step
// we would always pick the edge with minimal edge weight.
const edgesQueue = new PriorityQueue();
// Set of vertices that has been already visited.
const visitedVertices = {};
// Vertex from which we will start graph traversal.
const startVertex = graph.getAllVertices()[0];
// Add start vertex to the set of visited ones.
visitedVertices[startVertex.getKey()] = startVertex;
// Add all edges of start vertex to the queue.
startVertex.getEdges().forEach((graphEdge) => {
edgesQueue.add(graphEdge, graphEdge.weight);
});
// Now let's explore all queued edges.
while (!edgesQueue.isEmpty()) {
// Fetch next queued edge with minimal weight.
/** @var {GraphEdge} currentEdge */
const currentMinEdge = edgesQueue.poll();
// Find out the next unvisited minimal vertex to traverse.
let nextMinVertex = null;
if (!visitedVertices[currentMinEdge.startVertex.getKey()]) {
nextMinVertex = currentMinEdge.startVertex;
} else if (!visitedVertices[currentMinEdge.endVertex.getKey()]) {
nextMinVertex = currentMinEdge.endVertex;
}
// If all vertices of current edge has been already visited then skip this round.
if (nextMinVertex) {
// Add current min edge to MST.
minimumSpanningTree.addEdge(currentMinEdge);
// Add vertex to the set of visited ones.
visitedVertices[nextMinVertex.getKey()] = nextMinVertex;
// Add all current vertex's edges to the queue.
nextMinVertex.getEdges().forEach((graphEdge) => {
// Add only vertices that link to unvisited nodes.
if (
!visitedVertices[graphEdge.startVertex.getKey()] ||
!visitedVertices[graphEdge.endVertex.getKey()]
) {
edgesQueue.add(graphEdge, graphEdge.weight);
}
});
}
}
return minimumSpanningTree;
}