mirror of
https://github.moeyy.xyz/https://github.com/trekhleb/javascript-algorithms.git
synced 2024-12-26 23:21:18 +08:00
Add Kruskal.
This commit is contained in:
parent
cad8ccd9bb
commit
fc53c7de5d
@ -70,7 +70,7 @@
|
|||||||
* [Bellman-Ford Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/bellman-ford) - finding shortest path to all graph vertices
|
* [Bellman-Ford Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/bellman-ford) - finding shortest path to all graph vertices
|
||||||
* [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)
|
* [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)
|
||||||
* [Prim’s Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/prim) - finding Minimum Spanning Tree (MST) for weighted undirected graph
|
* [Prim’s Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/prim) - finding Minimum Spanning Tree (MST) for weighted undirected graph
|
||||||
* Kruskal’s Algorithm - finding Minimum Spanning Tree (MST)
|
* [Kruskal’s Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/kruskal) - finding Minimum Spanning Tree (MST) for weighted undirected graph
|
||||||
* Topological Sorting
|
* Topological Sorting
|
||||||
* Eulerian path, Eulerian circuit
|
* Eulerian path, Eulerian circuit
|
||||||
* Strongly Connected Component algorithm
|
* Strongly Connected Component algorithm
|
||||||
@ -85,7 +85,8 @@
|
|||||||
* **Greedy**
|
* **Greedy**
|
||||||
* [Unbound Knapsack Problem](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/knapsack-problem)
|
* [Unbound Knapsack Problem](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/knapsack-problem)
|
||||||
* [Dijkstra Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/dijkstra) - finding shortest path to all graph vertices
|
* [Dijkstra Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/dijkstra) - finding shortest path to all graph vertices
|
||||||
* [Prim’s Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/prim) - finding Minimum Spanning Tree (MST)
|
* [Prim’s Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/prim) - finding Minimum Spanning Tree (MST) for weighted undirected graph
|
||||||
|
* [Kruskal’s Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/kruskal) - finding Minimum Spanning Tree (MST) for weighted undirected graph
|
||||||
* **Divide and Conquer**
|
* **Divide and Conquer**
|
||||||
* [Euclidean Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/euclidean-algorithm) - calculate the Greatest Common Divisor (GCD)
|
* [Euclidean Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/euclidean-algorithm) - calculate the Greatest Common Divisor (GCD)
|
||||||
* [Permutations](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/permutations) (with and without repetitions)
|
* [Permutations](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/permutations) (with and without repetitions)
|
||||||
|
49
src/algorithms/graph/kruskal/README.md
Normal file
49
src/algorithms/graph/kruskal/README.md
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# Kruskal's Algorithm
|
||||||
|
|
||||||
|
Kruskal's algorithm is a minimum-spanning-tree algorithm which
|
||||||
|
finds an edge of the least possible weight that connects any two
|
||||||
|
trees in the forest. It is a greedy algorithm in graph theory
|
||||||
|
as it finds a minimum spanning tree for a connected weighted
|
||||||
|
graph adding increasing cost arcs at each step. This means it
|
||||||
|
finds a subset of the edges that forms a tree that includes every
|
||||||
|
vertex, where the total weight of all the edges in the tree is
|
||||||
|
minimized. If the graph is not connected, then it finds a
|
||||||
|
minimum spanning forest (a minimum spanning tree for each
|
||||||
|
connected component).
|
||||||
|
|
||||||
|
![Kruskal Algorithm](https://upload.wikimedia.org/wikipedia/commons/5/5c/MST_kruskal_en.gif)
|
||||||
|
|
||||||
|
![Kruskal Demo](https://upload.wikimedia.org/wikipedia/commons/b/bb/KruskalDemo.gif)
|
||||||
|
|
||||||
|
A demo for Kruskal's algorithm based on Euclidean distance.
|
||||||
|
|
||||||
|
## Minimum Spanning Tree
|
||||||
|
|
||||||
|
A **minimum spanning tree** (MST) or minimum weight spanning tree
|
||||||
|
is a subset of the edges of a connected, edge-weighted
|
||||||
|
(un)directed graph that connects all the vertices together,
|
||||||
|
without any cycles and with the minimum possible total edge
|
||||||
|
weight. That is, it is a spanning tree whose sum of edge weights
|
||||||
|
is as small as possible. More generally, any edge-weighted
|
||||||
|
undirected graph (not necessarily connected) has a minimum
|
||||||
|
spanning forest, which is a union of the minimum spanning
|
||||||
|
trees for its connected components.
|
||||||
|
|
||||||
|
![Minimum Spanning Tree](https://upload.wikimedia.org/wikipedia/commons/d/d2/Minimum_spanning_tree.svg)
|
||||||
|
|
||||||
|
A planar graph and its minimum spanning tree. Each edge is
|
||||||
|
labeled with its weight, which here is roughly proportional
|
||||||
|
to its length.
|
||||||
|
|
||||||
|
![Minimum Spanning Tree](https://upload.wikimedia.org/wikipedia/commons/c/c9/Multiple_minimum_spanning_trees.svg)
|
||||||
|
|
||||||
|
This figure shows there may be more than one minimum spanning
|
||||||
|
tree in a graph. In the figure, the two trees below the graph
|
||||||
|
are two possibilities of minimum spanning tree of the given graph.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [Minimum Spanning Tree on Wikipedia](https://en.wikipedia.org/wiki/Minimum_spanning_tree)
|
||||||
|
- [Kruskal's Algorithm on Wikipedia](https://en.wikipedia.org/wiki/Kruskal%27s_algorithm)
|
||||||
|
- [Kruskal's Algorithm on YouTube by Tushar Roy](https://www.youtube.com/watch?v=fAuF0EuZVCk)
|
||||||
|
- [Kruskal's Algorithm on YouTube by Michael Sambol](https://www.youtube.com/watch?v=71UQH7Pr9kU)
|
91
src/algorithms/graph/kruskal/__test__/kruskal.test.js
Normal file
91
src/algorithms/graph/kruskal/__test__/kruskal.test.js
Normal 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 kruskal from '../kruskal';
|
||||||
|
|
||||||
|
describe('kruskal', () => {
|
||||||
|
it('should fire an error for directed graph', () => {
|
||||||
|
function applyPrimToDirectedGraph() {
|
||||||
|
const graph = new Graph(true);
|
||||||
|
|
||||||
|
kruskal(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 = kruskal(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('E,C,A,B,D,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 = kruskal(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');
|
||||||
|
});
|
||||||
|
});
|
62
src/algorithms/graph/kruskal/kruskal.js
Normal file
62
src/algorithms/graph/kruskal/kruskal.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import Graph from '../../../data-structures/graph/Graph';
|
||||||
|
import QuickSort from '../../sorting/quick-sort/QuickSort';
|
||||||
|
import DisjointSet from '../../../data-structures/disjoint-set/DisjointSet';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Graph} graph
|
||||||
|
* @return {Graph}
|
||||||
|
*/
|
||||||
|
export default function kruskal(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();
|
||||||
|
|
||||||
|
// Sort all graph edges in increasing order.
|
||||||
|
const sortingCallbacks = {
|
||||||
|
/**
|
||||||
|
* @param {GraphEdge} graphEdgeA
|
||||||
|
* @param {GraphEdge} graphEdgeB
|
||||||
|
*/
|
||||||
|
compareCallback: (graphEdgeA, graphEdgeB) => {
|
||||||
|
if (graphEdgeA.weight === graphEdgeB.weight) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return graphEdgeA.weight <= graphEdgeB.weight ? -1 : 1;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const sortedEdges = new QuickSort(sortingCallbacks).sort(graph.getAllEdges());
|
||||||
|
|
||||||
|
// Create disjoint sets for all graph vertices.
|
||||||
|
const keyCallback = graphVertex => graphVertex.getKey();
|
||||||
|
const disjointSet = new DisjointSet(keyCallback);
|
||||||
|
|
||||||
|
graph.getAllVertices().forEach((graphVertex) => {
|
||||||
|
disjointSet.makeSet(graphVertex);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Go through all edges started from the minimum one and try to add them
|
||||||
|
// to minimum spanning tree. The criteria of adding the edge would be whether
|
||||||
|
// it is forms the cycle or not (if it connects two vertices from one disjoint
|
||||||
|
// set or not).
|
||||||
|
for (let edgeIndex = 0; edgeIndex < sortedEdges.length; edgeIndex += 1) {
|
||||||
|
/** @var {GraphEdge} currentEdge */
|
||||||
|
const currentEdge = sortedEdges[edgeIndex];
|
||||||
|
|
||||||
|
// Check if edge forms the cycle. If it does then skip it.
|
||||||
|
if (!disjointSet.inSameSet(currentEdge.startVertex, currentEdge.endVertex)) {
|
||||||
|
// Unite two subsets into one.
|
||||||
|
disjointSet.union(currentEdge.startVertex, currentEdge.endVertex);
|
||||||
|
|
||||||
|
// Add this edge to spanning tree.
|
||||||
|
minimumSpanningTree.addEdge(currentEdge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return minimumSpanningTree;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user