LeetCode #973 Medium

K Closest Points to Origin

K Closest Points to Origin is LeetCode 973 (Medium). You get an array points, where points[i] = [xᵢ, yᵢ] is a point on a plane, and an integer k. Return the k points closest to the origin (0, 0), using ordinary straight-line (Euclidean) distance.

  • The answer may be returned in any order, and it is unique apart from order.
  • There are 1 to 10⁴ points, with coordinates between −10⁴ and 10⁴.
Constraints
  • 1 <= k <= points.length <= 10⁴
  • -10⁴ <= xi, yi <= 10⁴
heaparraysortingdivide and conquer
Open on LeetCode ↗
02

Intuition

Two ideas make this fast.

  • Skip the square root: the distance is √(x² + y²), but you only ever compare two distances. A bigger x² + y² always means a bigger root, so comparing x² + y² gives the same order with exact whole numbers.
  • Keep only k candidates: hold the k closest points seen so far in a max-heap, which keeps the farthest of them on top. When a new point makes it k + 1, the top is the one point that cannot be among the k closest, so it comes out.

After every point has passed through, the heap holds exactly the k closest points to origin.

How to spot this pattern

"The k smallest, largest or closest of n items" calls for a heap of size k that keeps the worst survivor on top; this k closest points LeetCode problem is the classic example. Kth Largest Element in an Array (215) and Top K Frequent Elements (347) use the same pattern with a min-heap, because there the worst survivor is the smallest.

03

Approach

Try it first

Before reading on, take [[3,3],[5,-1],[-2,4]] with k = 2. Which point has to go, and which single comparison tells you?

1

Score each point by x² + y²

For a point (x, y) compute x * x + y * y. It ranks points exactly like the true distance, so it is all the comparison needs. With coordinates up to 10⁴ the largest score is 2 × 10⁸, which still fits in a 32-bit integer.

2

Push the point into a max-heap

In the k closest points Python code, heapq is a min-heap, so push the score as a negative number, (-score, x, y); the farthest point then sits on top. C++'s priority_queue is already a max-heap, and Java needs a comparator that puts larger scores first.

3

Pop the top when there are k + 1

If the heap now holds k + 1 points, pop the top. It is the farthest of those k + 1, so k points are closer than it and it can never be in the answer. The heap never grows past k + 1, which is where the log k comes from.

4

Return what is left

After the last point the heap holds exactly k points, the closest ones. The problem accepts any order, so read them straight out of the heap without sorting.

04

K Closest Points to Origin solution in Python | C++ | Java

▶1class Solution:
▶2 def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
▶3 heap = []
▶4 for x, y in points:
▶5 heapq.heappush(heap, (-(x * x + y * y), x, y))
▶6 if len(heap) > k:
▶7 heapq.heappop(heap)
▶8 return [[x, y] for _, x, y in heap]
pointsx²+y²(1,3)10(-2,2)8heap0 / 1heap empty, room for k = 1
k1
heapemptymax-heap on x²+y²
Start. Each point is scored by x² + y², which orders points exactly like their real distance. The heap will hold at most 1 point, with the farthest one on top, ready to be thrown out.
pointsx²+y²(1,3)10(-2,2)8heap(1,3)10top1 / 1push (1,3) with score 10
point(1,3)1² + 3² = 10
heap size1at most 1
Push (1,3). The heap holds 1 of at most 1, so nothing has to leave yet. Nothing can be ruled out until more than 1 point is held.
pointsx²+y²(1,3)10(-2,2)8heap(1,3)10(-2,2)8top2 / 1push (-2,2) with score 8 → 2 points
point(-2,2)-2² + 2² = 8
heap size2one too many
Push (-2,2). The heap now holds 2 points, one more than k, so one must go. The farthest of them is on top.
pointsx²+y²(1,3)10(-2,2)8heap(-2,2)8top1 / 1pop top (1,3) (score 10)
popped(1,3)score 10
heap(-2,2)1 closest so far
(1,3) was the farthest held, so it leaves. A closer point is held, so it can never be the closest. The new top is (-2,2).
pointsx²+y²(1,3)10(-2,2)8heap(-2,2)8top1 / 1return (-2,2)
answer(-2,2)
work2 pushes, 1 popsheap never above k + 1
Done. Every point has passed through the heap, and the 1 left are the closest. The order does not matter, so they are returned as they sit in the heap.
05

Sort by squared distance

Sort the points by x² + y², nearest first, and return the first k of them.

▶1class Solution:
▶2 def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
▶3 points.sort(key=lambda p: p[0] * p[0] + p[1] * p[1])
▶4 return points[:k]
06

Common pitfalls

Taking the square root

✗ Wrong
d = math.sqrt(x * x + y * y)
✓ Right
d = x * x + y * y

It gives the same order at extra cost, and it turns an exact whole-number comparison into a floating-point one.

Popping before pushing

✗ Wrong
if len(heap) == k:
    heapq.heappop(heap)
heapq.heappush(heap, (-d, x, y))
✓ Right
heapq.heappush(heap, (-d, x, y))
if len(heap) > k:
    heapq.heappop(heap)

Popping first throws out the farthest held point before the new one is compared with it. If the new point is even farther, it stays and a closer point is lost.

07

Complexity

Time
O(n log k)
Space
O(k)
Each of the n points costs one push and at most one pop on a heap that never holds more than k + 1 points.
08

K Closest Points to Origin FAQ

Can K Closest Points to Origin be solved in O(n)?

On average, yes, with quickselect: partition the points around a pivot score until the k closest sit at the front, recursing into one side only. It averages O(n) but can degrade to O(n²) with bad pivots, while the heap guarantees O(n log k).