Minimum spanning tree is the graph pattern for connecting all vertices with the least total edge cost while avoiding cycles. Strong candidates distinguish edge-centric and node-centric thinking, explain why each chosen edge safely expands the tree, and validate connectivity instead of assuming an MST always exists.
Interview lens
A strong explanation should name the invariant, the safe transition, and the condition that makes this pattern preferable to brute force.
Pattern Summary Table
| Pattern | When to Use | Key Idea | Example |
|---|---|---|---|
| 04 04 Minimum Spanning Tree Pattern | you need the cheapest way to connect every node in an undirected weighted graph | grow connectivity only through edges that safely expand the current forest or tree | Minimum Cost to Connect All Nodes |
Problem Statement
Given an undirected weighted graph, compute the minimum total cost needed to connect all nodes, or detect that the graph is disconnected.
Note
Emphasize the constraints before coding. The real signal is often whether the brute-force search space, update volume, or graph model makes the naive solution impossible.
Pattern Recognition Signals
- Keywords in the problem: connect all nodes, minimum total cost, spanning tree, no cycles.
- Structural signal: each chosen edge must connect two previously separate regions of the graph.
- Complexity signal: the optimized version avoids repeated rescans, recomputation, or state explosion that brute force would suffer.
Important
If the task is “connect everything as cheaply as possible” in an undirected weighted graph, think MST.
Java Template
public int kruskalMST(int n, int[][] edges) {
Arrays.sort(edges, Comparator.comparingInt(a -> a[2]));
DSU dsu = new DSU(n);
int cost = 0, used = 0;
for (int[] e : edges) {
if (dsu.union(e[0], e[1])) {
cost += e[2];
if (++used == n - 1) break;
}
}
return used == n - 1 ? cost : -1;
}
Dry Run (Kruskal)
Edges (u,v,w) sorted by weight:
(0,1,1), (1,2,2), (0,2,3), (2,3,4)
Process:
- take
(0,1,1)-> components merge, cost=1 - take
(1,2,2)-> merge, cost=3 - skip
(0,2,3)-> would form cycle - take
(2,3,4)-> merge, cost=7
Used edges = n-1, MST cost = 7.
Kruskal vs Prim
- Kruskal: edge-centric, great with edge list + DSU
- Prim: node-centric, great with adjacency list + priority queue
Rule of thumb:
- sparse edge list input -> Kruskal
- dense adjacency graph or incremental expansion view -> Prim
Both are O(E log E) / O(E log V) scale depending on representation.
Disconnected Graph Handling
MST exists only if graph is fully connected. Always validate:
return used == n - 1 ? cost : -1;
Skipping this check returns incorrect partial-forest cost.
Problem 1: Minimum Cost to Connect All Nodes
Problem description:
Given a connected weighted undirected graph, return the total weight of its minimum spanning tree, or -1 if the graph is disconnected.
What we are solving actually: We need one cheapest edge set that connects every node without cycles. The hidden challenge is expanding the tree only with edges that add a brand-new node.
What we are doing actually:
- Start Prim’s algorithm from any node.
- Use a min-heap to always choose the cheapest edge leaving the current tree.
- Ignore edges that point to already-included nodes.
- Count cost only when an edge truly expands the tree.
public int minimumSpanningTreeCost(int n, List<List<int[]>> graph) {
boolean[] used = new boolean[n];
PriorityQueue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(a -> a[1]));
pq.offer(new int[]{0, 0}); // {node, edgeWeight} with a zero-cost virtual start edge.
int cost = 0;
int seen = 0;
while (!pq.isEmpty() && seen < n) {
int[] cur = pq.poll();
int u = cur[0], w = cur[1];
if (used[u]) continue; // Skip stale edges that point back into the existing tree.
used[u] = true;
cost += w; // The cheapest edge that reaches a new node becomes part of the MST.
seen++;
for (int[] edge : graph.get(u)) {
int v = edge[0], weight = edge[1];
if (!used[v]) pq.offer(new int[]{v, weight}); // Offer every outward edge from the growing tree.
}
}
return seen == n ? cost : -1; // If some node was never reached, one spanning tree does not exist.
}
Debug steps:
- print heap entries as
{node, weight}to confirm the cheapest outgoing edge is chosen next - test one disconnected graph to ensure the method returns
-1 - verify the invariant that
costonly grows whenseengrows
Problem-Fit Checklist
- Identify whether input size or query count requires preprocessing or specialized data structures.
- Confirm problem constraints (sorted input, non-negative weights, DAG-only, immutable array, etc.).
- Validate that the pattern gives asymptotic improvement over brute-force under worst-case input.
- Define explicit success criteria: value only, index recovery, count, path reconstruction, or ordering.
Invariant and Reasoning
- Write one invariant that must stay true after every transition (loop step, recursion return, or update).
- Ensure each step makes measurable progress toward termination.
- Guard boundary states explicitly (empty input, singleton, duplicates, overflow, disconnected graph).
- Add a quick correctness check using a tiny hand-worked example before coding full solution.
Complexity and Design Notes
- Compute time complexity for both preprocessing and per-query/per-update operations.
- Track memory overhead and object allocations, not only Big-O notation.
- Prefer primitives and tight loops in hot paths to reduce GC pressure in Java.
- If multiple variants exist, choose the one with the simplest correctness proof first.
Production Perspective
- Convert algorithmic states into explicit metrics (queue size, active nodes, cache hit ratio, relaxation count).
- Add guardrails for pathological inputs to avoid latency spikes.
- Keep implementation deterministic where possible to simplify debugging and incident analysis.
- Separate pure algorithm logic from I/O and parsing so the core stays testable.
Implementation Workflow
- Implement the minimal correct template with clear invariants.
- Add edge-case tests before optimizing.
- Measure complexity-sensitive operations on realistic input sizes.
- Refactor for readability only after behavior is locked by tests.
Common Mistakes
- Choosing the pattern without proving problem fit.
- Ignoring edge cases (empty input, duplicates, overflow, disconnected state).
- Mixing multiple strategies without clear invariants.
- No complexity analysis against worst-case input.
Practice Set (Recommended Order)
- Min Cost to Connect All Points (LC 1584)
LeetCode - Connecting Cities With Minimum Cost (LC 1135)
LeetCode
Key Takeaways
- This pattern is most effective when transitions are explicit and invariants are enforced at every step.
- Strong preconditions and boundary handling make these implementations production-safe.
- Reuse this template and adapt it per problem constraints.
Categories
Tags
Comments