Closest Pair of Points
Problem Statement
Given an array of n points in a 2D plane, find the pair of points with the minimum Euclidean distance between them.
Example: Input: points = [(2, 3), (12, 30), (40, 50),
(5, 1), (12, 10), (3, 4)]
Output: Minimum distance = 1.414 (between points (2, 3) and (3, 4))
Explanation: Distance = √[(3-2)² + (4-3)²] = √2 ≈ 1.414
Approach 1: Brute Force
Explanation: Calculate the distance between every pair of points using nested loops and keep track of the minimum distance found.
Time Complexity: O(n2)
Space Complexity: O(1)
function distance(p1, p2):
return sqrt((p1.x - p2.x)² + (p1.y - p2.y)²)
function closestPair(points):
n = length(points)
minDist = infinity
for i = 0 to n-2:
for j = i+1 to n-1:
dist = distance(points[i], points[j])
if dist < minDist:
minDist = dist
pair = (points[i], points[j])
return minDist
💡 Think: Compute all pair distances; keep the minimum.
Approach 2: Sort by X-coordinate then Brute Force
Explanation: Sort points by x-coordinate first. For each point, only check points within a reasonable x-distance range, reducing unnecessary comparisons in practice.
Time Complexity: O(n2) worst case, better average case
Space Complexity: O(1)
function closestPair(points):
n = length(points)
sort(points by x-coordinate)
minDist = infinity
for i = 0 to n-2:
for j = i+1 to n-1:
// Early termination if x-distance exceeds minDist
if points[j].x - points[i].x >= minDist:
break
dist = distance(points[i], points[j])
if dist < minDist:
minDist = dist
pair = (points[i], points[j])
return minDist
💡 Think: Sort by x; skip pairs whose x-gap already exceeds current best.
Approach 3: Divide and Conquer
Explanation: Sort points by x-coordinate and divide into two halves. Recursively find closest pairs in both halves. The minimum distance could be in left half, right half, or across the dividing line. Check the strip near the dividing line efficiently.
Time Complexity: O(n log n) with optimization
Space Complexity: O(n)
function stripClosest(strip, d):
minDist = d
sort(strip by y-coordinate)
for i = 0 to length(strip)-1:
j = i + 1
while j < length(strip) and (strip[j].y - strip[i].y) < minDist:
minDist = min(minDist, distance(strip[i], strip[j]))
j = j + 1
return minDist
function closestPairRec(pointsX, pointsY):
n = length(pointsX)
// Base case: use brute force for small n
if n <= 3:
return bruteForce(pointsX)
// Divide
mid = n / 2
midPoint = pointsX[mid]
pointsYLeft = points from pointsY with x <= midPoint.x
pointsYRight = points from pointsY with x > midPoint.x
// Conquer
leftDist = closestPairRec(pointsX[0...mid], pointsYLeft)
rightDist = closestPairRec(pointsX[mid+1...n], pointsYRight)
// Find minimum of left and right
d = min(leftDist, rightDist)
// Build strip array containing points within distance d from midline
strip = []
for i = 0 to n-1:
if abs(pointsY[i].x - midPoint.x) < d:
strip.append(pointsY[i])
// Find closest points in strip
return min(d, stripClosest(strip, d))
function closestPair(points):
pointsX = sort(copy of points by x-coordinate)
pointsY = sort(copy of points by y-coordinate)
return closestPairRec(pointsX, pointsY)
💡 Think: Solve halves and check a narrow mid-strip in y-order for O(n log n).
The Divide and Conquer approach is the optimal solution with O(n log n) time complexity when properly optimized. It efficiently divides the problem into smaller subproblems and handles the merge step by checking only relevant points in the strip area.