THN Interview Prep

1584. Min Cost to Connect All Points

At a Glance

Problem (one-liner)

Given 2D integer points, cost between any pair is Manhattan distance. Return minimum total edge weight to make graph connected (spanning tree).

Recognition Cues

  • Complete graph implicit — too many edges to materialize all
  • MST — Prim grows tree with min edge to frontier; Kruskal sorts edges

Diagram

At-a-glance flow (replace with problem-specific Mermaid as you refine this note). camelCase node IDs; no spaces in IDs.

Loading diagram…

Approaches

  • All pairs edges + KruskalO(n^2 log n) with sorting.
  • Optimal — Prim with lazy heap — O(n^2 log n) similar; O(n^2) possible with clever nearest arrays.

Optimal Solution

Go

import (
	"container/heap"
)

type primEdge struct {
	point    int
	distance int
}

type primHeap []primEdge

func (heapSlice primHeap) Len() int { return len(heapSlice) }
func (heapSlice primHeap) Less(left, right int) bool {
	return heapSlice[left].distance < heapSlice[right].distance
}
func (heapSlice primHeap) Swap(left, right int) {
	heapSlice[left], heapSlice[right] = heapSlice[right], heapSlice[left]
}

func (heapSlice *primHeap) Push(value any) {
	*heapSlice = append(*heapSlice, value.(primEdge))
}

func (heapSlice *primHeap) Pop() any {
	old := *heapSlice
	length := len(old)
	item := old[length-1]
	*heapSlice = old[0 : length-1]
	return item
}

func manhattan(left []int, right []int) int {
	dx := left[0] - right[0]
	dy := left[1] - right[1]
	if dx < 0 {
		dx = -dx
	}
	if dy < 0 {
		dy = -dy
	}
	return dx + dy
}

func minCostConnectPoints(points [][]int) int {
	nodeCount := len(points)
	visited := make([]bool, nodeCount)
	minHeap := &primHeap{}
	heap.Init(minHeap)
	heap.Push(minHeap, primEdge{point: 0, distance: 0})
	total := 0
	edgesUsed := 0

	for minHeap.Len() > 0 && edgesUsed < nodeCount {
		current := heap.Pop(minHeap).(primEdge)
		if visited[current.point] {
			continue
		}
		visited[current.point] = true
		total += current.distance
		edgesUsed++
		for neighbor := 0; neighbor < nodeCount; neighbor++ {
			if visited[neighbor] {
				continue
			}
			distance := manhattan(points[current.point], points[neighbor])
			heap.Push(minHeap, primEdge{point: neighbor, distance: distance})
		}
	}
	return total
}

JavaScript

class MinHeap {
  constructor() {
    this.data = [];
  }

  size() {
    return this.data.length;
  }

  push(item) {
    this.data.push(item);
    this.bubbleUp(this.data.length - 1);
  }

  pop() {
    if (this.data.length === 0) {
      return undefined;
    }
    const top = this.data[0];
    const last = this.data.pop();
    if (this.data.length > 0) {
      this.data[0] = last;
      this.bubbleDown(0);
    }
    return top;
  }

  bubbleUp(index) {
    while (index > 0) {
      const parentIndex = Math.floor((index - 1) / 2);
      if (this.data[index].distance >= this.data[parentIndex].distance) {
        break;
      }
      this.swap(index, parentIndex);
      index = parentIndex;
    }
  }

  bubbleDown(index) {
    const length = this.data.length;
    while (true) {
      const leftChild = index * 2 + 1;
      const rightChild = index * 2 + 2;
      let smallest = index;
      if (leftChild < length && this.data[leftChild].distance < this.data[smallest].distance) {
        smallest = leftChild;
      }
      if (rightChild < length && this.data[rightChild].distance < this.data[smallest].distance) {
        smallest = rightChild;
      }
      if (smallest === index) {
        break;
      }
      this.swap(index, smallest);
      index = smallest;
    }
  }

  swap(leftIndex, rightIndex) {
    const temp = this.data[leftIndex];
    this.data[leftIndex] = this.data[rightIndex];
    this.data[rightIndex] = temp;
  }
}

function manhattan(left, right) {
  return Math.abs(left[0] - right[0]) + Math.abs(left[1] - right[1]);
}

function minCostConnectPoints(points) {
  const nodeCount = points.length;
  const visited = new Array(nodeCount).fill(false);
  const heap = new MinHeap();
  heap.push({ point: 0, distance: 0 });
  let total = 0;
  let edgesUsed = 0;

  while (heap.size() > 0 && edgesUsed < nodeCount) {
    const current = heap.pop();
    if (visited[current.point]) {
      continue;
    }
    visited[current.point] = true;
    total += current.distance;
    edgesUsed += 1;
    for (let neighbor = 0; neighbor < nodeCount; neighbor += 1) {
      if (visited[neighbor]) {
        continue;
      }
      const distance = manhattan(points[current.point], points[neighbor]);
      heap.push({ point: neighbor, distance });
    }
  }
  return total;
}

Walkthrough

Prim starts arbitrary root (0), repeatedly attaches cheapest edge from growing tree to new point until n nodes included.

Invariant: Partial MST property — cheapest connecting edge chosen each step with non-negative weights (Manhattan).

Edge Cases

  • Two points — their Manhattan distance
  • Collinear points

Pitfalls

  • Double-count edges — Prim adds distance only when node first visited (skip stale heap entries)

Similar Problems

Variants

  • Euclidean MST — different metric; often Kruskal with spatial partitions

Mind-Map Tags

#mst #prim #manhattan #complete-graph #greedy

Last updated on

Spotted something unclear or wrong on this page?

On this page