K Closest Points to Origin
Medium · Heap / Priority Queue · Explanation and code example
Quick Select
How it works
We want the k closest points, but we do NOT need them sorted.
This is a perfect use-case for QuickSelect, the same idea used in QuickSort's partition step:
- Pick a pivot point.
- Partition all points into:
- points closer than the pivot
- points farther than the pivot
- After partitioning, the pivot ends at its correct position in the final sorted order.
- If the pivot ends up at index
p:- If
p == k, then the left side already contains thekclosest points. - If
p < k, search the right half. - If
p > k, search the left half.
- If
This avoids fully sorting the array and runs in average O(N) time.
Steps
- Define a function to compute squared distance:
dist = x^2 + y^2. - Use a partition function:
- Choose a pivot distance.
- Rearrange points so all smaller distances go left, larger go right.
- Return the pivot's final index.
- Maintain two pointers:
L = 0,R = n - 1. - Repeatedly partition:
- If pivot index
p == k, stop. - If
p < k, moveL = p + 1. - If
p > k, moveR = p - 1.
- If pivot index
- After partitioning ends, the first
kpoints in the array are thekclosest. - Return those
kpoints.
Complexity
- Time complexity:
O(n)in average case,O(n ^ 2)in worst case. - Space complexity:
O(1)
Things to watch for
Computing Actual Distance with Square Root
Using sqrt(x^2 + y^2) is unnecessary and introduces floating-point precision issues. Since we only compare relative distances, squared distance x^2 + y^2 preserves ordering and avoids costly square root operations.
Using the Wrong Heap Type
For the heap approach, using a min-heap requires extracting k elements at the end, while a max-heap of size k naturally keeps the k closest. Mixing these up leads to incorrect results or inefficient solutions that maintain more elements than needed.
Integer Overflow in Distance Calculation
When coordinates can be large (up to 10^4), squaring them produces values up to 10^8. While this fits in a 32-bit integer, summing two such values approaches the limit. In languages with overflow concerns, ensure your distance calculation uses appropriate integer types.
Python code example
class Solution:
def kClosest(self, points, k):
euclidean = lambda x: x[0] ** 2 + x[1] ** 2
def partition(l, r):
pivotIdx = r
pivotDist = euclidean(points[pivotIdx])
i = l
for j in range(l, r):
if euclidean(points[j]) <= pivotDist:
points[i], points[j] = points[j], points[i]
i += 1
points[i], points[r] = points[r], points[i]
return i
L, R = 0, len(points) - 1
pivot = len(points)
while pivot != k:
pivot = partition(L, R)
if pivot < k:
L = pivot + 1
else:
R = pivot - 1
return points[:k]
C++ code example
class Solution {
public:
vector<vector<int>> kClosest(vector<vector<int>>& points, int k) {
int L = 0, R = points.size() - 1;
int pivot = points.size();
while (pivot != k) {
pivot = partition(points, L, R);
if (pivot < k) {
L = pivot + 1;
} else {
R = pivot - 1;
}
}
return vector<std::vector<int>>(points.begin(), points.begin() + k);
}
private:
int partition(vector<vector<int>>& points, int l, int r) {
int pivotIdx = r;
int pivotDist = euclidean(points[pivotIdx]);
int i = l;
for (int j = l; j < r; j++) {
if (euclidean(points[j]) <= pivotDist) {
swap(points[i], points[j]);
i++;
}
}
swap(points[i], points[r]);
return i;
}
int euclidean(vector<int>& point) {
return point[0] * point[0] + point[1] * point[1];
}
};
Source and license
Adapted from NeetCode’s source · MIT license. Node types are supplied by LeetCode where needed.