Kth Smallest Pair Distance finds the k-th smallest value among all absolute pair differences.
For an array , each pair with has distance:
Instead of generating all distances, sort the array and binary search over possible distance values.
Problem
Given an array of length and a 1-based rank , find the k-th smallest pair distance.
There are:
pairs.
Algorithm
Sort the array. Then binary search the answer distance . For each candidate , count how many pairs have distance at most .
kth_smallest_pair_distance(A, k):
sort A
low = 0
high = A[n - 1] - A[0]
while low < high:
mid = floor((low + high) / 2)
if count_pairs_at_most(A, mid) < k:
low = mid + 1
else:
high = mid
return lowThe counting step uses two pointers.
count_pairs_at_most(A, d):
count = 0
left = 0
for right from 0 to n - 1:
while A[right] - A[left] > d:
left = left + 1
count = count + (right - left)
return countExample
Let:
After sorting:
Pair distances are:
For , the answer is:
Correctness
After sorting, the absolute distance for a pair with is .
For a fixed right endpoint, the two pointer loop finds the smallest left index such that all indices from left to right - 1 form pairs with distance at most . Therefore it adds exactly the number of valid pairs ending at right.
The predicate “number of pairs with distance at most is at least ” is monotone. If it holds for a distance , it also holds for every larger distance. Binary search returns the smallest distance for which the predicate holds, which is exactly the k-th smallest pair distance.
Complexity
| step | cost |
|---|---|
| sorting | |
| one count pass | |
| binary search over distance range | |
| total | |
| space | extra, aside from sorting |
Here:
When to Use
Use this algorithm when all pair distances are too many to materialize, but the values are numeric and sortable.
It is especially useful when is large enough that distance generation is infeasible.
Implementation
def count_pairs_at_most(a, d):
count = 0
left = 0
for right in range(len(a)):
while a[right] - a[left] > d:
left += 1
count += right - left
return count
def kth_smallest_pair_distance(a, k):
a.sort()
low = 0
high = a[-1] - a[0]
while low < high:
mid = (low + high) // 2
if count_pairs_at_most(a, mid) < k:
low = mid + 1
else:
high = mid
return lowfunc countPairsAtMost(a []int, d int) int {
count := 0
left := 0
for right := 0; right < len(a); right++ {
for a[right]-a[left] > d {
left++
}
count += right - left
}
return count
}
func KthSmallestPairDistance(a []int, k int) int {
sortInts(a)
low := 0
high := a[len(a)-1] - a[0]
for low < high {
mid := low + (high-low)/2
if countPairsAtMost(a, mid) < k {
low = mid + 1
} else {
high = mid
}
}
return low
}