94.Min Cost to Connect All Points
Problem
You are given an array points where points[i] = [xi, yi] is a point on a 2D plane. The cost of connecting two points is their Manhattan distance: |xi - xj| + |yi - yj|.
Return the minimum total cost to connect all points so that there is exactly one simple path between any two points (i.e. the connections form a spanning tree).
Examples
Input: points = [[0,0],[2,2],[3,10],[5,2],[7,0]]
Output: 20
Explanation: Connect (0,0)-(2,2)=4, (2,2)-(5,2)=3, (5,2)-(7,0)=4, (2,2)-(3,10)=9. Total = 20.Input: points = [[3,12],[-2,5],[-4,1]]
Output: 18
Explanation: (3,12)-(-2,5)=12 and (-2,5)-(-4,1)=6.Input: points = [[0,0]]
Output: 0Constraints
1 <= points.length <= 1000-10^6 <= xi, yi <= 10^6- All pairs
(xi, yi)are distinct.
💡 Key Insight
This is a Minimum Spanning Tree on a complete graph: every pair of points is a potential edge, so E = n(n-1)/2 ≈ 500,000. Kruskal and heap-based Prim both work, but they pay log factors on half a million edges. Because the graph is dense, the array-based Prim's algorithm — which keeps a minDist[] to the growing tree and scans for the closest outside point — runs in O(n²) with O(n) memory and never materialises the edge list.
Kruskal's Algorithm
TimeO(n² log n)SpaceO(n²)Intuition
Generate every pair (i, j) with its Manhattan distance, sort the edges by cost, and add them cheapest first. Use Union-Find to skip any edge whose endpoints are already connected (it would form a cycle). Stop after n - 1 edges.
Algorithm
- Build all
n(n-1)/2edges[cost, i, j]and sort bycost. - Initialise Union-Find over
npoints. - For each edge in order: if
union(i, j)succeeds, addcostand incrementused; stop whenused == n - 1. - Return the total.
Visualization
points: A(0,0) B(2,2) C(3,10) D(5,2) E(7,0)
sorted edges: B-D 3, A-B 4, D-E 4, A-D 7, B-E 7, ... B-C 9, C-D 10, ...
take B-D 3 {B,D}
take A-B 4 {A,B,D}
take D-E 4 {A,B,D,E}
skip A-D 7 (cycle) skip B-E 7 (cycle) ...
take B-C 9 {A,B,C,D,E} -> 4 edges, total = 20Code
import java.util.Arrays;
class Solution {
private int[] parent;
public int minCostConnectPoints(int[][] points) {
int n = points.length;
int[][] edges = new int[n * (n - 1) / 2][];
int k = 0;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
int cost = Math.abs(points[i][0] - points[j][0]) + Math.abs(points[i][1] - points[j][1]);
edges[k++] = new int[] {cost, i, j};
}
}
Arrays.sort(edges, (a, b) -> Integer.compare(a[0], b[0]));
parent = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = i;
}
int total = 0;
int used = 0;
for (int[] e : edges) {
if (used == n - 1) {
break; // spanning tree complete
}
int rootA = find(e[1]);
int rootB = find(e[2]);
if (rootA != rootB) {
parent[rootA] = rootB;
total += e[0];
used++;
}
}
return total;
}
private int find(int x) {
while (parent[x] != x) {
parent[x] = parent[parent[x]]; // path halving
x = parent[x];
}
return x;
}
}Complexity
- Time: O(n² log n) — sorting
n²/2edges dominates. - Space: O(n²) — the full edge list.
Prim's Algorithm with a Min-Heap
TimeO(n² log n)SpaceO(n²)Intuition
Grow the tree from point 0. A min-heap holds candidate edges (cost, point) leaving the tree; pop the cheapest, and if that point is not yet in the tree, add it and push edges from it to every point still outside. Stale heap entries for points already in the tree are skipped (lazy deletion).
Algorithm
inTree[]all false; push(0, 0).- While fewer than
npoints are in the tree: pop(cost, u); skip ifinTree[u]. - Mark
u, addcosttototal, and push(dist(u, v), v)for everyvnot in the tree. - Return
total.
Visualization
flowchart LR
A["A (0,0)"] -->|"4"| B["B (2,2)"]
B -->|"3"| D["D (5,2)"]
D -->|"4"| E["E (7,0)"]
B -->|"9"| C["C (3,10)"]pop (0,A) tree={A} push B:4 C:13 D:7 E:7
pop (4,B) tree={A,B} push C:9 D:3 E:7
pop (3,D) tree={A,B,D} push C:10 E:4
pop (4,E) tree={A,B,D,E} push C:14
pop (7,D) stale, (7,E) stale, (7,E) stale
pop (9,C) tree={A,B,C,D,E} total = 0+4+3+4+9 = 20Code
import java.util.PriorityQueue;
class Solution {
public int minCostConnectPoints(int[][] points) {
int n = points.length;
boolean[] inTree = new boolean[n];
// heap of {cost, point}
PriorityQueue<int[]> heap = new PriorityQueue<>((a, b) -> Integer.compare(a[0], b[0]));
heap.offer(new int[] {0, 0});
int total = 0;
int added = 0;
while (added < n) {
int[] top = heap.poll();
int u = top[1];
if (inTree[u]) {
continue; // stale entry
}
inTree[u] = true;
total += top[0];
added++;
for (int v = 0; v < n; v++) {
if (!inTree[v]) {
int cost = Math.abs(points[u][0] - points[v][0]) + Math.abs(points[u][1] - points[v][1]);
heap.offer(new int[] {cost, v});
}
}
}
return total;
}
}Complexity
- Time: O(n² log n) — up to
n²heap pushes, eachO(log n²) = O(log n). - Space: O(n²) — the heap can hold that many stale edges.
Prim's Algorithm with an ArrayOptimal
TimeO(n²)SpaceO(n)Intuition
In a dense graph the heap is wasted effort. Keep minDist[v] = cheapest known edge from the tree to v. Each round, linearly scan for the outside point with the smallest minDist, add it, then relax minDist for all other outside points using the new point. That is n rounds of O(n) work, with no heap and no edge list.
Algorithm
minDist[]filled withInteger.MAX_VALUE,minDist[0] = 0.- Repeat
ntimes:- Pick the unvisited
uwith the smallestminDist[u]; mark it, addminDist[u]tototal. - For every unvisited
v,minDist[v] = min(minDist[v], dist(u, v)).
- Pick the unvisited
- Return
total.
Dry Run
points: A(0,0) B(2,2) C(3,10) D(5,2) E(7,0)
A B C D E pick
start 0 ∞ ∞ ∞ ∞ A (+0)
after A - 4 13 7 7 B (+4)
after B - - 9 3 7 D (+3)
after D - - 9 - 4 E (+4)
after E - - 9 - - C (+9)
total = 20Code
import java.util.Arrays;
class Solution {
public int minCostConnectPoints(int[][] points) {
int n = points.length;
int[] minDist = new int[n];
Arrays.fill(minDist, Integer.MAX_VALUE);
minDist[0] = 0;
boolean[] inTree = new boolean[n];
int total = 0;
for (int round = 0; round < n; round++) {
// pick the closest point not yet in the tree
int u = -1;
for (int v = 0; v < n; v++) {
if (!inTree[v] && (u == -1 || minDist[v] < minDist[u])) {
u = v;
}
}
inTree[u] = true;
total += minDist[u];
// relax distances using the newly added point
for (int v = 0; v < n; v++) {
if (!inTree[v]) {
int cost = Math.abs(points[u][0] - points[v][0]) + Math.abs(points[u][1] - points[v][1]);
if (cost < minDist[v]) {
minDist[v] = cost;
}
}
}
}
return total;
}
}Complexity
- Time: O(n²) —
nrounds, each scanning all points twice. - Space: O(n) —
minDistandinTreearrays.
Complexity Comparison
| Approach | Time | Space |
|---|---|---|
| Kruskal's Algorithm | O(n² log n) | O(n²) |
| Prim's with Min-Heap | O(n² log n) | O(n²) |
| Prim's with Array | O(n²) | O(n) |
Common Pitfalls
- Using Euclidean distance instead of Manhattan distance.
- Forgetting the single-point case — the answer is
0, and Kruskal must not allocate a negative-size array (n(n-1)/2 = 0is fine). - In heap-based Prim, not skipping stale entries for points already in the tree, which double-counts costs.
- Assuming a sparse-graph algorithm is fastest here — with
n²edges, theO(n²)array version wins.