Skip to content

Commit fc53c7d

Browse files
committed
Add Kruskal.
1 parent cad8ccd commit fc53c7d

File tree

4 files changed

+205
-2
lines changed

4 files changed

+205
-2
lines changed

README.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
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)
7272
* [Prim’s Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/prim) - finding Minimum Spanning Tree (MST) for weighted undirected graph
73-
* Kruskal’s Algorithm - finding Minimum Spanning Tree (MST)
73+
* [Kruskal’s Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/kruskal) - finding Minimum Spanning Tree (MST) for weighted undirected graph
7474
* Topological Sorting
7575
* Eulerian path, Eulerian circuit
7676
* Strongly Connected Component algorithm
@@ -85,7 +85,8 @@
8585
* **Greedy**
8686
* [Unbound Knapsack Problem](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/knapsack-problem)
8787
* [Dijkstra Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/dijkstra) - finding shortest path to all graph vertices
88-
* [Prim’s Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/prim) - finding Minimum Spanning Tree (MST)
88+
* [Prim’s Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/prim) - finding Minimum Spanning Tree (MST) for weighted undirected graph
89+
* [Kruskal’s Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/kruskal) - finding Minimum Spanning Tree (MST) for weighted undirected graph
8990
* **Divide and Conquer**
9091
* [Euclidean Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/euclidean-algorithm) - calculate the Greatest Common Divisor (GCD)
9192
* [Permutations](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/permutations) (with and without repetitions)
+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Kruskal's Algorithm
2+
3+
Kruskal's algorithm is a minimum-spanning-tree algorithm which
4+
finds an edge of the least possible weight that connects any two
5+
trees in the forest. It is a greedy algorithm in graph theory
6+
as it finds a minimum spanning tree for a connected weighted
7+
graph adding increasing cost arcs at each step. This means it
8+
finds a subset of the edges that forms a tree that includes every
9+
vertex, where the total weight of all the edges in the tree is
10+
minimized. If the graph is not connected, then it finds a
11+
minimum spanning forest (a minimum spanning tree for each
12+
connected component).
13+
14+
![Kruskal Algorithm](https://upload.wikimedia.org/wikipedia/commons/5/5c/MST_kruskal_en.gif)
15+
16+
![Kruskal Demo](https://upload.wikimedia.org/wikipedia/commons/b/bb/KruskalDemo.gif)
17+
18+
A demo for Kruskal's algorithm based on Euclidean distance.
19+
20+
## Minimum Spanning Tree
21+
22+
A **minimum spanning tree** (MST) or minimum weight spanning tree
23+
is a subset of the edges of a connected, edge-weighted
24+
(un)directed graph that connects all the vertices together,
25+
without any cycles and with the minimum possible total edge
26+
weight. That is, it is a spanning tree whose sum of edge weights
27+
is as small as possible. More generally, any edge-weighted
28+
undirected graph (not necessarily connected) has a minimum
29+
spanning forest, which is a union of the minimum spanning
30+
trees for its connected components.
31+
32+
![Minimum Spanning Tree](https://upload.wikimedia.org/wikipedia/commons/d/d2/Minimum_spanning_tree.svg)
33+
34+
A planar graph and its minimum spanning tree. Each edge is
35+
labeled with its weight, which here is roughly proportional
36+
to its length.
37+
38+
![Minimum Spanning Tree](https://upload.wikimedia.org/wikipedia/commons/c/c9/Multiple_minimum_spanning_trees.svg)
39+
40+
This figure shows there may be more than one minimum spanning
41+
tree in a graph. In the figure, the two trees below the graph
42+
are two possibilities of minimum spanning tree of the given graph.
43+
44+
## References
45+
46+
- [Minimum Spanning Tree on Wikipedia](https://en.wikipedia.org/wiki/Minimum_spanning_tree)
47+
- [Kruskal's Algorithm on Wikipedia](https://en.wikipedia.org/wiki/Kruskal%27s_algorithm)
48+
- [Kruskal's Algorithm on YouTube by Tushar Roy](https://www.youtube.com/watch?v=fAuF0EuZVCk)
49+
- [Kruskal's Algorithm on YouTube by Michael Sambol](https://www.youtube.com/watch?v=71UQH7Pr9kU)
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 kruskal from '../kruskal';
5+
6+
describe('kruskal', () => {
7+
it('should fire an error for directed graph', () => {
8+
function applyPrimToDirectedGraph() {
9+
const graph = new Graph(true);
10+
11+
kruskal(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 = kruskal(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('E,C,A,B,D,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 = kruskal(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+
});
+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import Graph from '../../../data-structures/graph/Graph';
2+
import QuickSort from '../../sorting/quick-sort/QuickSort';
3+
import DisjointSet from '../../../data-structures/disjoint-set/DisjointSet';
4+
5+
/**
6+
* @param {Graph} graph
7+
* @return {Graph}
8+
*/
9+
export default function kruskal(graph) {
10+
// It should fire error if graph is directed since the algorithm works only
11+
// for undirected graphs.
12+
if (graph.isDirected) {
13+
throw new Error('Prim\'s algorithms works only for undirected graphs');
14+
}
15+
16+
// Init new graph that will contain minimum spanning tree of original graph.
17+
const minimumSpanningTree = new Graph();
18+
19+
// Sort all graph edges in increasing order.
20+
const sortingCallbacks = {
21+
/**
22+
* @param {GraphEdge} graphEdgeA
23+
* @param {GraphEdge} graphEdgeB
24+
*/
25+
compareCallback: (graphEdgeA, graphEdgeB) => {
26+
if (graphEdgeA.weight === graphEdgeB.weight) {
27+
return 1;
28+
}
29+
30+
return graphEdgeA.weight <= graphEdgeB.weight ? -1 : 1;
31+
},
32+
};
33+
const sortedEdges = new QuickSort(sortingCallbacks).sort(graph.getAllEdges());
34+
35+
// Create disjoint sets for all graph vertices.
36+
const keyCallback = graphVertex => graphVertex.getKey();
37+
const disjointSet = new DisjointSet(keyCallback);
38+
39+
graph.getAllVertices().forEach((graphVertex) => {
40+
disjointSet.makeSet(graphVertex);
41+
});
42+
43+
// Go through all edges started from the minimum one and try to add them
44+
// to minimum spanning tree. The criteria of adding the edge would be whether
45+
// it is forms the cycle or not (if it connects two vertices from one disjoint
46+
// set or not).
47+
for (let edgeIndex = 0; edgeIndex < sortedEdges.length; edgeIndex += 1) {
48+
/** @var {GraphEdge} currentEdge */
49+
const currentEdge = sortedEdges[edgeIndex];
50+
51+
// Check if edge forms the cycle. If it does then skip it.
52+
if (!disjointSet.inSameSet(currentEdge.startVertex, currentEdge.endVertex)) {
53+
// Unite two subsets into one.
54+
disjointSet.union(currentEdge.startVertex, currentEdge.endVertex);
55+
56+
// Add this edge to spanning tree.
57+
minimumSpanningTree.addEdge(currentEdge);
58+
}
59+
}
60+
61+
return minimumSpanningTree;
62+
}

0 commit comments

Comments
 (0)