LeetCode #1584 Medium

Min Cost to Connect All Points

Min Cost to Connect All Points: connect every point with minimum total Manhattan edge cost.

Constraints
  • 1 <= points.length <= 1000
  • -10⁶ <= xi, yi <= 10⁶
  • All pairs (xi, yi) are distinct.
graphminimum-spanning-treeprim
Open on LeetCode ↗
02

Intuition

Min cost to connect all points joins every point on a plane at minimum total cost, where connecting two points costs their Manhattan distance. The greedy instinct — link each point to its nearest neighbour — fails, producing disconnected clusters or redundant cycles rather than one connected structure. The reframing is to see the geometry as a graph. Treat every point as a vertex and every pair as an edge weighted by Manhattan distance, giving a complete graph. Connecting all vertices for minimum total weight, with no redundant edges, is by definition a minimum spanning tree. Once named, the algorithm is standard. Prim's suits this case particularly well: - The graph is complete, so edges vastly outnumber vertices, and Prim's cost depends on vertices rather than on materialising all n² edges. That matters practically. Kruskal's would require sorting every one of the n(n−1)/2 edges, which is wasteful when most will never be considered. Prim's grows one tree, computing distances from each newly absorbed point only as needed. The correctness rests on the cut property: for any partition of the vertices, the lightest edge crossing it belongs to some minimum spanning tree. The tree-versus-rest split is exactly such a partition, so the cheapest edge leaving the tree is always safe to take. Stop once n vertices have joined. Stale heap entries pointing at already-visited points are skipped rather than removed.

How to spot this pattern

Whenever all locations must be connected and edge costs are additive, consider a minimum spanning tree. A dense implicit graph often favors Prim's algorithm because edges can be generated as vertices enter the tree.

03

Approach

Try it first

Before reading on: price up what the direct approach costs here, then ask which edge is always safe to take across a cut. Aim for O(n^2 log n) time and O(n^2) space.

1

See why nearest-neighbour fails

Linking each point to its closest neighbour creates disconnected clusters and cycles rather than one spanning structure. Recognising that pushes you toward a real MST algorithm instead of a local rule.

2

Model the points as a complete graph

Every pair of points is an edge weighted by Manhattan distance |x1-x2| + |y1-y2|. Connecting all vertices at minimum total weight is the definition of a minimum spanning tree — naming it settles the algorithm.

3

Prefer Prim's for a dense graph

With n(n−1)/2 edges, Kruskal's would sort them all. Prim's cost scales with vertices and computes distances on demand from each absorbed point, which is far better suited to a complete graph.

4

Seed the heap at any point

Push (0, 0) — point zero joins at no cost. The starting choice is arbitrary, since a minimum spanning tree spans every vertex regardless of where the growth begins.

5

Take the cheapest crossing edge

Pop the minimum, skip it if the endpoint is already visited, otherwise add its cost and mark the point. The cut property guarantees the cheapest edge leaving the tree belongs to some MST, so no accepted edge is ever wrong.

6

Push distances from each newly added point

After absorbing a point, compute its Manhattan distance to every unvisited point and push those into the heap. Stale entries for points absorbed in the meantime are discarded when popped.

7

Cost of the approach

Each of the n absorptions pushes up to n edges, giving O(n² log n) time with a heap and O(n²) space. The O(n²) array-based Prim's without a heap is actually faster here — worth mentioning for a genuinely complete graph.

04

Solution & live demo

▶1class Solution:
▶2 def minCostConnectPoints(self, points:
▶3 List[List[int]]) -> int:
▶4 n = len(points)
▶5 heap = [(0, 0)]
▶6 visited = set()
▶7 total = 0
▶8 
▶9 while len(visited) < n:
▶10 cost, point = heappop(heap)
▶11 if point in visited:
▶12 continue
▶13 visited.add(point)
▶14 total += cost
▶15 x1, y1 = points[point]
▶16 for neighbor, (x2, y2) in enumerate(points):
▶17 if neighbor not in visited:
▶18 distance = abs(x1 - x2) + abs(y1 - y2)
▶19 heappush(heap, (distance, neighbor))
▶20 return total
05

Common pitfalls

Adding costs for stale heap entries

✗ Wrong
cost, point = heappop(heap)
total += cost
✓ Right
cost, point = heappop(heap)
if point in visited:
    continue
total += cost

Multiple candidate edges can lead to the same vertex, but an MST accepts it only once.

Using Euclidean distance

✗ Wrong
distance = sqrt(dx * dx + dy * dy)
✓ Right
distance = abs(dx) + abs(dy)

The problem explicitly assigns Manhattan edge costs.

Stopping when the heap first empties incorrectly

✗ Wrong
while heap and len(visited) < n - 1:
✓ Right
while heap and len(visited) < n:

All n vertices, including the starting point, must be accepted.

06

Edge cases

Only one point

The initial zero-cost entry visits it and the result remains zero.

Several heap entries target the same point

The visited check accepts only the cheapest first entry and discards stale ones.

Negative coordinates

Absolute coordinate differences compute Manhattan distance without special handling.

07

Complexity

Time
O(n^2 log n)
Space
O(n^2)
This direct Prim implementation may retain many candidate edges for the complete graph.