Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 55a01f2

Browse files
authoredJun 16, 2023
feat: add kruskal algorithm (TheAlgorithms#137)
* feat: add kruskal algorithm * Rewrite existing tests and add test for minimum spanning forest
1 parent b738c80 commit 55a01f2

File tree

2 files changed

+154
-0
lines changed

2 files changed

+154
-0
lines changed
 

‎graph/kruskal.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { DisjointSet } from '../data_structures/disjoint_set/disjoint_set';
2+
3+
/**
4+
* @function kruskal
5+
* @description Compute a minimum spanning forest of a weighted undirected graph
6+
* @Complexity_Analysis
7+
* Time complexity: O(Elog(V))
8+
* Space Complexity: O(V)
9+
* @param {Edge[]} edges - The edges of the graph
10+
* @param {number} num_vertices - The number of vertices in the graph
11+
* @return {Edge[], number} - [The edges of the minimum spanning tree, the sum of the weights of the edges in the tree]
12+
* @see https://en.wikipedia.org/wiki/Kruskal%27s_algorithm
13+
*/
14+
export const kruskal = (edges: Edge[], num_vertices: number): [Edge[], number] => {
15+
let cost = 0;
16+
let minimum_spanning_tree = [];
17+
18+
// Use a disjoint set to quickly join sets and find if vertices live in different sets
19+
let sets = new DisjointSet(num_vertices);
20+
21+
// Sort the edges in ascending order by weight so that we can greedily add cheaper edges to the tree
22+
edges.sort((a, b) => a.weight - b.weight);
23+
24+
for (let edge of edges) {
25+
if (sets.find(edge.a) !== sets.find(edge.b)) {
26+
// Node A and B live in different sets. Add edge(a, b) to the tree and join the nodes' sets together.
27+
minimum_spanning_tree.push(edge);
28+
cost += edge.weight;
29+
sets.join(edge.a, edge.b);
30+
}
31+
}
32+
33+
return [minimum_spanning_tree, cost];
34+
}
35+
36+
export class Edge {
37+
a: number = 0;
38+
b: number = 0;
39+
weight: number = 0;
40+
constructor(a: number, b: number, weight: number) {
41+
this.a = a;
42+
this.b = b;
43+
this.weight = weight;
44+
}
45+
}

‎graph/test/kruskal.test.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { Edge, kruskal } from "../kruskal";
2+
3+
let test_graph = (expected_tree_edges: Edge[], other_edges: Edge[], num_vertices: number, expected_cost: number) => {
4+
let [tree_edges, cost] = kruskal(expected_tree_edges.concat(other_edges), num_vertices);
5+
expect(cost).toStrictEqual(expected_cost);
6+
for (let expected_edge of expected_tree_edges) {
7+
expect(tree_edges.includes(expected_edge)).toBeTruthy();
8+
}
9+
for (let unexpected_edge of other_edges) {
10+
expect(tree_edges.includes(unexpected_edge)).toBeFalsy();
11+
}
12+
};
13+
14+
15+
describe("kruskal", () => {
16+
17+
it("should return empty tree for empty graph", () => {
18+
expect(kruskal([], 0)).toStrictEqual([[], 0]);
19+
});
20+
21+
it("should return empty tree for single element graph", () => {
22+
expect(kruskal([], 1)).toStrictEqual([[], 0]);
23+
});
24+
25+
it("should return correct value for two element graph", () => {
26+
const edge = new Edge(0, 1, 5);
27+
expect(kruskal([edge], 2)).toStrictEqual([[edge], 5]);
28+
});
29+
30+
it("should return the correct value", () => {
31+
let expected_tree_edges = [
32+
new Edge(0, 1, 1),
33+
new Edge(1, 3, 2),
34+
new Edge(2, 3, 3),
35+
];
36+
37+
let other_edges = [
38+
new Edge(0, 2, 4),
39+
new Edge(0, 3, 5),
40+
new Edge(1, 2, 6),
41+
];
42+
43+
test_graph(expected_tree_edges, other_edges, 7, 6);
44+
});
45+
46+
it("should return the correct value", () => {
47+
let expected_tree_edges = [
48+
new Edge(0, 2, 2),
49+
new Edge(1, 3, 9),
50+
new Edge(2, 6, 74),
51+
new Edge(2, 7, 8),
52+
new Edge(3, 4, 3),
53+
new Edge(4, 9, 9),
54+
new Edge(5, 7, 5),
55+
new Edge(7, 9, 4),
56+
new Edge(8, 9, 2),
57+
]
58+
59+
let other_edges = [
60+
new Edge(0, 1, 10),
61+
new Edge(2, 4, 47),
62+
new Edge(4, 5, 42),
63+
];
64+
65+
test_graph(expected_tree_edges, other_edges, 10, 116);
66+
});
67+
68+
})
69+
70+
describe("kruskal forest", () => {
71+
it("should return empty tree for forest of 2 node trees", () => {
72+
let edges = [new Edge(0, 1, 10), new Edge(2, 3, 15)];
73+
test_graph(edges, [], 4, 25);
74+
});
75+
76+
it("should return the correct value", () => {
77+
let expected_tree_edges = [
78+
// Tree 1
79+
new Edge(0, 2, 2),
80+
new Edge(1, 3, 9),
81+
new Edge(2, 6, 74),
82+
new Edge(2, 7, 8),
83+
new Edge(3, 4, 3),
84+
new Edge(4, 9, 9),
85+
new Edge(5, 7, 5),
86+
new Edge(7, 9, 4),
87+
new Edge(8, 9, 2),
88+
89+
// Tree 2
90+
new Edge(10, 11, 1),
91+
new Edge(11, 13, 2),
92+
new Edge(12, 13, 3),
93+
]
94+
95+
let other_edges = [
96+
// Tree 1
97+
new Edge(0, 1, 10),
98+
new Edge(2, 4, 47),
99+
new Edge(4, 5, 42),
100+
101+
// Tree 2
102+
new Edge(10, 12, 4),
103+
new Edge(10, 13, 5),
104+
new Edge(11, 12, 6),
105+
];
106+
107+
test_graph(expected_tree_edges, other_edges, 14, 122);
108+
});
109+
});

0 commit comments

Comments
 (0)
Please sign in to comment.