Skip to content

Commit cad8ccd

Browse files
committedMay 7, 2018
Add Prim.
1 parent 50df3bf commit cad8ccd

File tree

9 files changed

+194
-2
lines changed

9 files changed

+194
-2
lines changed
 

‎README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
* [Dijkstra Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/dijkstra) - finding shortest path to all graph vertices
7070
* [Bellman-Ford Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/bellman-ford) - finding shortest path to all graph vertices
7171
* [Detect Cycle](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/detect-cycle) - for both directed and undirected graphs (DFS and Disjoint Set based versions)
72-
* [Prim’s Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/prim) - finding Minimum Spanning Tree (MST)
72+
* [Prim’s Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/prim) - finding Minimum Spanning Tree (MST) for weighted undirected graph
7373
* Kruskal’s Algorithm - finding Minimum Spanning Tree (MST)
7474
* Topological Sorting
7575
* Eulerian path, Eulerian circuit

‎src/algorithms/graph/prim/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,4 @@ are two possibilities of minimum spanning tree of the given graph.
4444
- [Minimum Spanning Tree on Wikipedia](https://en.wikipedia.org/wiki/Minimum_spanning_tree)
4545
- [Prim's Algorithm on Wikipedia](https://en.wikipedia.org/wiki/Prim%27s_algorithm)
4646
- [Prim's Algorithm on YouTube by Tushar Roy](https://www.youtube.com/watch?v=oP2-8ysT3QQ)
47+
- [Prim's Algorithm on YouTube by Michael Sambol](https://www.youtube.com/watch?v=cplfcGZmX7I)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import GraphVertex from '../../../../data-structures/graph/GraphVertex';
2+
import GraphEdge from '../../../../data-structures/graph/GraphEdge';
3+
import Graph from '../../../../data-structures/graph/Graph';
4+
import prim from '../prim';
5+
6+
describe('prim', () => {
7+
it('should fire an error for directed graph', () => {
8+
function applyPrimToDirectedGraph() {
9+
const graph = new Graph(true);
10+
11+
prim(graph);
12+
}
13+
14+
expect(applyPrimToDirectedGraph).toThrowError();
15+
});
16+
17+
it('should find minimum spanning tree', () => {
18+
const vertexA = new GraphVertex('A');
19+
const vertexB = new GraphVertex('B');
20+
const vertexC = new GraphVertex('C');
21+
const vertexD = new GraphVertex('D');
22+
const vertexE = new GraphVertex('E');
23+
const vertexF = new GraphVertex('F');
24+
const vertexG = new GraphVertex('G');
25+
26+
const edgeAB = new GraphEdge(vertexA, vertexB, 2);
27+
const edgeAD = new GraphEdge(vertexA, vertexD, 3);
28+
const edgeAC = new GraphEdge(vertexA, vertexC, 3);
29+
const edgeBC = new GraphEdge(vertexB, vertexC, 4);
30+
const edgeBE = new GraphEdge(vertexB, vertexE, 3);
31+
const edgeDF = new GraphEdge(vertexD, vertexF, 7);
32+
const edgeEC = new GraphEdge(vertexE, vertexC, 1);
33+
const edgeEF = new GraphEdge(vertexE, vertexF, 8);
34+
const edgeFG = new GraphEdge(vertexF, vertexG, 9);
35+
const edgeFC = new GraphEdge(vertexF, vertexC, 6);
36+
37+
const graph = new Graph();
38+
39+
graph
40+
.addEdge(edgeAB)
41+
.addEdge(edgeAD)
42+
.addEdge(edgeAC)
43+
.addEdge(edgeBC)
44+
.addEdge(edgeBE)
45+
.addEdge(edgeDF)
46+
.addEdge(edgeEC)
47+
.addEdge(edgeEF)
48+
.addEdge(edgeFC)
49+
.addEdge(edgeFG);
50+
51+
expect(graph.getWeight()).toEqual(46);
52+
53+
const minimumSpanningTree = prim(graph);
54+
55+
expect(minimumSpanningTree.getWeight()).toBe(24);
56+
expect(minimumSpanningTree.getAllVertices().length).toBe(graph.getAllVertices().length);
57+
expect(minimumSpanningTree.getAllEdges().length).toBe(graph.getAllVertices().length - 1);
58+
expect(minimumSpanningTree.toString()).toBe('A,B,D,C,E,F,G');
59+
});
60+
61+
it('should find minimum spanning tree for simple graph', () => {
62+
const vertexA = new GraphVertex('A');
63+
const vertexB = new GraphVertex('B');
64+
const vertexC = new GraphVertex('C');
65+
const vertexD = new GraphVertex('D');
66+
67+
const edgeAB = new GraphEdge(vertexA, vertexB, 1);
68+
const edgeAD = new GraphEdge(vertexA, vertexD, 3);
69+
const edgeBC = new GraphEdge(vertexB, vertexC, 1);
70+
const edgeBD = new GraphEdge(vertexB, vertexD, 3);
71+
const edgeCD = new GraphEdge(vertexC, vertexD, 1);
72+
73+
const graph = new Graph();
74+
75+
graph
76+
.addEdge(edgeAB)
77+
.addEdge(edgeAD)
78+
.addEdge(edgeBC)
79+
.addEdge(edgeBD)
80+
.addEdge(edgeCD);
81+
82+
expect(graph.getWeight()).toEqual(9);
83+
84+
const minimumSpanningTree = prim(graph);
85+
86+
expect(minimumSpanningTree.getWeight()).toBe(3);
87+
expect(minimumSpanningTree.getAllVertices().length).toBe(graph.getAllVertices().length);
88+
expect(minimumSpanningTree.getAllEdges().length).toBe(graph.getAllVertices().length - 1);
89+
expect(minimumSpanningTree.toString()).toBe('A,B,C,D');
90+
});
91+
});

‎src/algorithms/graph/prim/prim.js

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import Graph from '../../../data-structures/graph/Graph';
2+
import PriorityQueue from '../../../data-structures/priority-queue/PriorityQueue';
3+
4+
/**
5+
* @param {Graph} graph
6+
* @return {Graph}
7+
*/
8+
export default function prim(graph) {
9+
// It should fire error if graph is directed since the algorithm works only
10+
// for undirected graphs.
11+
if (graph.isDirected) {
12+
throw new Error('Prim\'s algorithms works only for undirected graphs');
13+
}
14+
15+
// Init new graph that will contain minimum spanning tree of original graph.
16+
const minimumSpanningTree = new Graph();
17+
18+
// This priority queue will contain all the edges that are starting from
19+
// visited nodes and they will be ranked by edge weight - so that on each step
20+
// we would always pick the edge with minimal edge weight.
21+
const edgesQueue = new PriorityQueue();
22+
23+
// Set of vertices that has been already visited.
24+
const visitedVertices = {};
25+
26+
// Vertex from which we will start graph traversal.
27+
const startVertex = graph.getAllVertices()[0];
28+
29+
// Add start vertex to the set of visited ones.
30+
visitedVertices[startVertex.getKey()] = startVertex;
31+
32+
// Add all edges of start vertex to the queue.
33+
startVertex.getEdges().forEach((graphEdge) => {
34+
edgesQueue.add(graphEdge, graphEdge.weight);
35+
});
36+
37+
// Now let's explore all queued edges.
38+
while (!edgesQueue.isEmpty()) {
39+
// Fetch next queued edge with minimal weight.
40+
/** @var {GraphEdge} currentEdge */
41+
const currentMinEdge = edgesQueue.poll();
42+
43+
// Find out the next unvisited minimal vertex to traverse.
44+
let nextMinVertex = null;
45+
if (!visitedVertices[currentMinEdge.startVertex.getKey()]) {
46+
nextMinVertex = currentMinEdge.startVertex;
47+
} else if (!visitedVertices[currentMinEdge.endVertex.getKey()]) {
48+
nextMinVertex = currentMinEdge.endVertex;
49+
}
50+
51+
// If all vertices of current edge has been already visited then skip this round.
52+
if (nextMinVertex) {
53+
// Add current min edge to MST.
54+
minimumSpanningTree.addEdge(currentMinEdge);
55+
56+
// Add vertex to the set of visited ones.
57+
visitedVertices[nextMinVertex.getKey()] = nextMinVertex;
58+
59+
// Add all current vertex's edges to the queue.
60+
nextMinVertex.getEdges().forEach((graphEdge) => {
61+
// Add only vertices that link to unvisited nodes.
62+
if (
63+
!visitedVertices[graphEdge.startVertex.getKey()] ||
64+
!visitedVertices[graphEdge.endVertex.getKey()]
65+
) {
66+
edgesQueue.add(graphEdge, graphEdge.weight);
67+
}
68+
});
69+
}
70+
}
71+
72+
return minimumSpanningTree;
73+
}

‎src/data-structures/graph/Graph.js

+6
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,18 @@ export default class Graph {
110110
return null;
111111
}
112112

113+
/**
114+
* @return {number}
115+
*/
113116
getWeight() {
114117
return this.getAllEdges().reduce((weight, graphEdge) => {
115118
return weight + graphEdge.weight;
116119
}, 0);
117120
}
118121

122+
/**
123+
* @return {string}
124+
*/
119125
toString() {
120126
return Object.keys(this.vertices).toString();
121127
}

‎src/data-structures/graph/GraphEdge.js

+7
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,11 @@ export default class GraphEdge {
1919

2020
return `${startVertexKey}_${endVertexKey}`;
2121
}
22+
23+
/**
24+
* @return {string}
25+
*/
26+
toString() {
27+
return this.getKey();
28+
}
2229
}

‎src/data-structures/graph/GraphVertex.js

+10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import LinkedList from '../linked-list/LinkedList';
22

33
export default class GraphVertex {
4+
/**
5+
* @param {*} value
6+
*/
47
constructor(value) {
58
if (value === undefined) {
69
throw new Error('Graph vertex must have a value');
@@ -37,6 +40,13 @@ export default class GraphVertex {
3740
return edges.map(neighborsConverter);
3841
}
3942

43+
/**
44+
* @return {GraphEdge[]}
45+
*/
46+
getEdges() {
47+
return this.edges.toArray().map(linkedListNode => linkedListNode.value);
48+
}
49+
4050
/**
4151
* @param {GraphEdge} requiredEdge
4252
* @returns {boolean}

‎src/data-structures/graph/__test__/GraphEdge.test.js

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ describe('GraphEdge', () => {
88
const edge = new GraphEdge(startVertex, endVertex);
99

1010
expect(edge.getKey()).toBe('A_B');
11+
expect(edge.toString()).toBe('A_B');
1112
expect(edge.startVertex).toEqual(startVertex);
1213
expect(edge.endVertex).toEqual(endVertex);
1314
expect(edge.weight).toEqual(0);

‎src/data-structures/graph/__test__/GraphVertex.test.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,20 @@ describe('GraphVertex', () => {
2121
expect(vertex.toString()).toBe('A');
2222
expect(vertex.getKey()).toBe('A');
2323
expect(vertex.edges.toString()).toBe('');
24+
expect(vertex.getEdges()).toEqual([]);
2425
});
2526

2627
it('should add edges to vertex and check if it exists', () => {
2728
const vertexA = new GraphVertex('A');
28-
const vertexB = new GraphVertex('A');
29+
const vertexB = new GraphVertex('B');
2930

3031
const edgeAB = new GraphEdge(vertexA, vertexB);
3132
vertexA.addEdge(edgeAB);
3233

3334
expect(vertexA.hasEdge(edgeAB)).toBeTruthy();
3435
expect(vertexB.hasEdge(edgeAB)).toBeFalsy();
36+
expect(vertexA.getEdges().length).toBe(1);
37+
expect(vertexA.getEdges()[0].toString()).toBe('A_B');
3538
});
3639

3740
it('should return vertex neighbors in case if current node is start one', () => {

0 commit comments

Comments
 (0)
Please sign in to comment.