Skip to main content
Spanning tree algorithms find connected subgraphs that include all vertices with minimum total edge weight.

getMinimumSpanningTree

function getMinimumSpanningTree<N, E>(
  graph: Graph<N, E>,
  opts?: MSTOptions<E>
): Graph<N, E>
Returns a minimum spanning tree of the graph. Only meaningful for connected undirected graphs (or the component reachable from an arbitrary start node in directed graphs). Complexity:
  • Prim’s algorithm: O(E log V) with binary heap
  • Kruskal’s algorithm: O(E log E) which is O(E log V) since E ≤ V²
When to use:
  • Network design (minimum cable length)
  • Clustering algorithms
  • Approximating traveling salesman
  • Finding minimum cost to connect all nodes
Algorithm: Uses Prim’s algorithm by default, or Kruskal’s with algorithm: 'kruskal' option.

Example

import { createGraph, getMinimumSpanningTree } from '@statelyai/graph';

const graph = createGraph({
  type: 'undirected',
  nodes: [{ id: 'a' }, { id: 'b' }, { id: 'c' }],
  edges: [
    { id: 'ab', sourceId: 'a', targetId: 'b', data: { weight: 1 } },
    { id: 'bc', sourceId: 'b', targetId: 'c', data: { weight: 2 } },
    { id: 'ac', sourceId: 'a', targetId: 'c', data: { weight: 3 } },
  ],
});

const mst = getMinimumSpanningTree(graph, {
  getWeight: (e) => e.data.weight,
});
// MST has edges 'ab' and 'bc' (total weight 3)

Unweighted graphs

For unweighted graphs (or when all edges have equal weight), the MST is any spanning tree:
const graph = createGraph({
  type: 'undirected',
  nodes: [{ id: 'a' }, { id: 'b' }, { id: 'c' }, { id: 'd' }],
  edges: [
    { id: 'ab', sourceId: 'a', targetId: 'b' },
    { id: 'bc', sourceId: 'b', targetId: 'c' },
    { id: 'cd', sourceId: 'c', targetId: 'd' },
    { id: 'ad', sourceId: 'a', targetId: 'd' },
  ],
});

const mst = getMinimumSpanningTree(graph);
// Default weight of 1 per edge
console.log('MST edges:', mst.edges.length); // 3 edges (V-1)

Using Kruskal’s algorithm

const mst = getMinimumSpanningTree(graph, {
  algorithm: 'kruskal',
  getWeight: (e) => e.data?.cost ?? 1,
});

Use Cases

Network design

Minimize cable length to connect all buildings:
import { createGraph, getMinimumSpanningTree } from '@statelyai/graph';

const buildings = createGraph({
  type: 'undirected',
  nodes: [
    { id: 'building-a' },
    { id: 'building-b' },
    { id: 'building-c' },
    { id: 'building-d' },
  ],
  edges: [
    { id: 'ab', sourceId: 'building-a', targetId: 'building-b', 
      data: { distance: 100 } },
    { id: 'ac', sourceId: 'building-a', targetId: 'building-c', 
      data: { distance: 150 } },
    { id: 'ad', sourceId: 'building-a', targetId: 'building-d', 
      data: { distance: 200 } },
    { id: 'bc', sourceId: 'building-b', targetId: 'building-c', 
      data: { distance: 120 } },
    { id: 'bd', sourceId: 'building-b', targetId: 'building-d', 
      data: { distance: 180 } },
    { id: 'cd', sourceId: 'building-c', targetId: 'building-d', 
      data: { distance: 140 } },
  ],
});

const network = getMinimumSpanningTree(buildings, {
  getWeight: (e) => e.data.distance,
});

const totalLength = network.edges.reduce(
  (sum, e) => sum + e.data.distance, 
  0
);
console.log(`Minimum cable length: ${totalLength}m`);
console.log('Connections:');
network.edges.forEach(e => {
  console.log(`  ${e.sourceId} - ${e.targetId}: ${e.data.distance}m`);
});

Clustering

Create hierarchical clusters by removing MST edges:
const points = createGraph({
  type: 'undirected',
  nodes: [
    { id: 'p1', data: { x: 0, y: 0 } },
    { id: 'p2', data: { x: 1, y: 1 } },
    { id: 'p3', data: { x: 10, y: 10 } },
    { id: 'p4', data: { x: 11, y: 11 } },
  ],
  edges: [] // fully connected with distances
});

// Add all pairwise edges with distances
for (let i = 0; i < points.nodes.length; i++) {
  for (let j = i + 1; j < points.nodes.length; j++) {
    const p1 = points.nodes[i];
    const p2 = points.nodes[j];
    const dist = Math.hypot(p1.data.x - p2.data.x, p1.data.y - p2.data.y);
    points.edges.push({
      id: `${p1.id}-${p2.id}`,
      sourceId: p1.id,
      targetId: p2.id,
      data: { distance: dist },
    });
  }
}

const mst = getMinimumSpanningTree(points, {
  getWeight: (e) => e.data.distance,
});

// Sort MST edges by weight and remove longest edges to create clusters
const sortedEdges = [...mst.edges].sort(
  (a, b) => b.data.distance - a.data.distance
);

const k = 2; // number of clusters
const clusterEdges = sortedEdges.slice(k - 1);
console.log('Cluster edges:', clusterEdges.map(e => `${e.sourceId}-${e.targetId}`));

isTree

function isTree(graph: Graph): boolean
Checks whether the graph is a tree (connected and acyclic). Complexity: O(V + E) When to use:
  • Validating tree structures
  • Checking if MST is valid
  • Ensuring graph has tree properties
Properties: A tree has exactly V-1 edges, is connected, and contains no cycles.

Example

import { createGraph, isTree } from '@statelyai/graph';

const tree = createGraph({
  nodes: [{ id: 'a' }, { id: 'b' }, { id: 'c' }],
  edges: [
    { id: 'ab', sourceId: 'a', targetId: 'b' },
    { id: 'ac', sourceId: 'a', targetId: 'c' },
  ],
});

isTree(tree); // true

Validating spanning tree

const mst = getMinimumSpanningTree(graph);

if (isTree(mst)) {
  console.log('Valid spanning tree');
  console.log('Edges:', mst.edges.length);
  console.log('Expected:', mst.nodes.length - 1);
}

Tree properties

function validateTreeProperties(graph: Graph): void {
  const isValidTree = isTree(graph);
  const expectedEdges = graph.nodes.length - 1;
  const actualEdges = graph.edges.length;
  
  console.log('Is tree:', isValidTree);
  console.log('Nodes:', graph.nodes.length);
  console.log('Edges:', actualEdges, '(expected:', expectedEdges, ')');
  
  if (!isValidTree) {
    if (actualEdges > expectedEdges) {
      console.log('Has cycle (too many edges)');
    } else if (actualEdges < expectedEdges) {
      console.log('Disconnected (too few edges)');
    }
  }
}

Options

MSTOptions

interface MSTOptions<TEdgeData = any> {
  algorithm?: 'prim' | 'kruskal';
  getWeight?: (edge: GraphEdge<TEdgeData>) => number;
}
Properties:
  • algorithm - Algorithm to use. Default: 'prim'. Options:
    • 'prim' - Prim’s algorithm (grows tree from single node)
    • 'kruskal' - Kruskal’s algorithm (sorts edges, uses union-find)
  • getWeight - Edge weight function. Default: every edge has weight 1

Algorithm comparison

AlgorithmBest forComplexityNotes
PrimDense graphsO(E log V)Grows tree from start node
KruskalSparse graphsO(E log E)Processes edges in weight order
Both produce the same MST (though ties may differ).

Build docs developers (and LLMs) love