|
| 1 | +import UnionFind from './structs/union-find'; |
| 2 | +import MinBinaryHeap from './structs/binary-heap'; |
| 3 | +import { Graph, IEdge, IMSTAlgorithm, IMSTAlgorithmOpt } from './types'; |
| 4 | +import { clone } from '@antv/util'; |
| 5 | + |
| 6 | +/** |
| 7 | +Calculates the Minimum Spanning Tree (MST) of a graph using the Prim's algorithm.The MST is a subset of edges that forms a tree connecting all nodes with the minimum possible total edge weight. |
| 8 | +@param graph - The graph for which the MST needs to be calculated. |
| 9 | +@param weightProps - Optional. The property name in the edge data object that represents the weight of the edge.If provided, the algorithm will consider the weight of edges based on this property.If not provided, the algorithm will assume all edges have a weight of 0. |
| 10 | +@returns An array of selected edges that form the Minimum Spanning Tree (MST) of the graph. |
| 11 | +*/ |
| 12 | +const primMST: IMSTAlgorithm = (graph, weightProps?) => { |
| 13 | + const selectedEdges: IEdge[] = []; |
| 14 | + const nodes = graph.getAllNodes(); |
| 15 | + const edges = graph.getAllEdges(); |
| 16 | + if (nodes.length === 0) { |
| 17 | + return selectedEdges; |
| 18 | + } |
| 19 | + // From the first node |
| 20 | + const currNode = nodes[0]; |
| 21 | + const visited = new Set(); |
| 22 | + visited.add(currNode); |
| 23 | + // Using binary heap to maintain the weight of edges from other nodes that have joined the node |
| 24 | + const compareWeight = (a: IEdge, b: IEdge) => { |
| 25 | + if (weightProps) { |
| 26 | + a.data; |
| 27 | + return (a.data[weightProps] as number) - (b.data[weightProps] as number); |
| 28 | + } |
| 29 | + return 0; |
| 30 | + }; |
| 31 | + const edgeQueue = new MinBinaryHeap<IEdge>(compareWeight); |
| 32 | + |
| 33 | + graph.getRelatedEdges(currNode.id, 'both').forEach((edge) => { |
| 34 | + edgeQueue.insert(edge); |
| 35 | + }); |
| 36 | + while (!edgeQueue.isEmpty()) { |
| 37 | + // Select the node with the least edge weight between the added node and the added node |
| 38 | + const currEdge: IEdge = edgeQueue.delMin(); |
| 39 | + const source = currEdge.source; |
| 40 | + const target = currEdge.target; |
| 41 | + if (visited.has(source) && visited.has(target)) continue; |
| 42 | + selectedEdges.push(currEdge); |
| 43 | + if (!visited.has(source)) { |
| 44 | + visited.add(source); |
| 45 | + graph.getRelatedEdges(source, 'both').forEach((edge) => { |
| 46 | + edgeQueue.insert(edge); |
| 47 | + }); |
| 48 | + } |
| 49 | + if (!visited.has(target)) { |
| 50 | + visited.add(target); |
| 51 | + graph.getRelatedEdges(target, 'both').forEach((edge) => { |
| 52 | + edgeQueue.insert(edge); |
| 53 | + }); |
| 54 | + } |
| 55 | + } |
| 56 | + return selectedEdges; |
| 57 | +}; |
| 58 | + |
| 59 | +/** |
| 60 | +Calculates the Minimum Spanning Tree (MST) of a graph using the Kruskal's algorithm.The MST is a subset of edges that forms a tree connecting all nodes with the minimum possible total edge weight. |
| 61 | +@param graph - The graph for which the MST needs to be calculated. |
| 62 | +@param weightProps - Optional. The property name in the edge data object that represents the weight of the edge.If provided, the algorithm will consider the weight of edges based on this property.If not provided, the algorithm will assume all edges have a weight of 0. |
| 63 | +@returns An array of selected edges that form the Minimum Spanning Tree (MST) of the graph. |
| 64 | +*/ |
| 65 | +const kruskalMST: IMSTAlgorithm = (graph, weightProps?) => { |
| 66 | + const selectedEdges: IEdge[] = []; |
| 67 | + const nodes = graph.getAllNodes(); |
| 68 | + const edges = graph.getAllEdges(); |
| 69 | + if (nodes.length === 0) { |
| 70 | + return selectedEdges; |
| 71 | + } |
| 72 | + // If you specify weight, all edges are sorted by weight from smallest to largest |
| 73 | + const weightEdges = clone(edges); |
| 74 | + if (weightProps) { |
| 75 | + weightEdges.sort((a: IEdge, b: IEdge) => { |
| 76 | + return (a.data[weightProps] as number) - (b.data[weightProps] as number); |
| 77 | + }); |
| 78 | + } |
| 79 | + const disjointSet = new UnionFind(nodes.map((n) => n.id)); |
| 80 | + // Starting with the edge with the least weight, if the two nodes connected by this edge are not in the same connected component in graph G, the edge is added. |
| 81 | + while (weightEdges.length > 0) { |
| 82 | + const curEdge = weightEdges.shift(); |
| 83 | + const source = curEdge.source; |
| 84 | + const target = curEdge.target; |
| 85 | + if (!disjointSet.connected(source, target)) { |
| 86 | + selectedEdges.push(curEdge); |
| 87 | + disjointSet.union(source, target); |
| 88 | + } |
| 89 | + } |
| 90 | + return selectedEdges; |
| 91 | +}; |
| 92 | + |
| 93 | +/** |
| 94 | +Calculates the Minimum Spanning Tree (MST) of a graph using either Prim's or Kruskal's algorithm.The MST is a subset of edges that forms a tree connecting all nodes with the minimum possible total edge weight. |
| 95 | +@param graph - The graph for which the MST needs to be calculated. |
| 96 | +@param weightProps - Optional. The property name in the edge data object that represents the weight of the edge.If provided, the algorithm will consider the weight of edges based on this property.If not provided, the algorithm will assume all edges have a weight of 0. |
| 97 | +@param algo - Optional. The algorithm to use for calculating the MST. Can be either 'prim' for Prim's algorithm, 'kruskal' for Kruskal's algorithm, or undefined to use the default algorithm (Kruskal's algorithm). |
| 98 | +@returns An array of selected edges that form the Minimum Spanning Tree (MST) of the graph. |
| 99 | +*/ |
| 100 | +export const minimumSpanningTree = (graph: Graph, weightProps?: string, algo?: 'prim' | 'kruskal' | undefined): IEdge[] => { |
| 101 | + const algos: IMSTAlgorithmOpt = { |
| 102 | + 'prim': primMST, |
| 103 | + 'kruskal': kruskalMST, |
| 104 | + }; |
| 105 | + return (algo && algos[algo](graph, weightProps)) || kruskalMST(graph, weightProps); |
| 106 | +}; |
0 commit comments