Heap · Top-k with a max-heap of size k
points where points[i] = [x, y] is a point on the plane, and an integer k. Return the k points closest to the origin (0, 0), using straight-line (Euclidean) distance. The answer may be in any order, and it is guaranteed to be unique.
You have a list of points on a map, and you stand at the centre, the origin. Which k points are closest to you?
(x, y) is the straight-line distance √(x² + y²).k points: the ones with the smallest distance.Two ideas help. First, we never need the square root. If one number is smaller than another, so is its square root, so we can compare x² + y² and stay with whole numbers. Second, the answer is just a ranking: a point is in the answer only if fewer than k points are closer. Here are the squared distances of five points, with k = 2.
The direct idea: sort all points by distance and take the first k.
Sorting takes about n log n steps. If k is 1, that is a lot of work to find one smallest point. And it puts all n points in order when we only care about k of them.
The sorted list is a ranking, and we throw away everything after position k. So what we really need is a way to always know the worst point we are currently keeping. While scanning the points, how do we find the farthest of our k keepers in an instant?
Turn the goal around. Instead of choosing the k closest, we keep a group of at most k points and throw out the farthest whenever the group gets too big. A heap is a box that always hands us the biggest (or smallest) item first, and removes it cheaply. We want the biggest distance first, so we use a "max-heap".
Python's heapq only has a min-heap, so we store the distance as a negative number: the smallest negative number is the biggest distance.
Why throwing out the top is safe. When the heap holds k + 1 points, its top is farther than the other k. So at least k points are closer than it, and it cannot be among the k closest. Removing it loses nothing. Note that the point we throw out may be the very one we just added.
dist = x² + y² and add (−dist, x, y) to the heap.k closest points.By hand, with the points (3,3) (1,1) (5,−1) (0,2) (−2,4) and k = 2. Their squared distances are 18, 2, 26, 4 and 20:
The answer is the points with distances 2 and 4: (1,1) and (0,2). Press Play to watch the heap. The red top is the point that gets taken out when the heap grows past k.
These are the questions that usually come next. Open one to see an answer you could say out loud.
What they are checkingWhether you see that the heap solution never needs the whole list. This is where it clearly beats sorting.
A strong answerYes. The heap is already built one point at a time: a new point is added, and if the heap is too big, the farthest is taken out. At any moment, the heap holds the k closest so far. It uses O(k) memory no matter how many points have arrived, and each new point costs O(log k).
What they are checkingWhether you know the other standard tool for "the k smallest", and what it trades away.
A strong answerYes, with quickselect. It works like quicksort, but after splitting the points around a pivot distance, it continues on only the side that contains position k. On average that takes O(n) time. The costs: it needs all the points in memory at once, it changes the order of the list, and its worst case is O(n²) unless the pivot is chosen at random. So I would use the heap for a stream or a very large list that does not fit in memory, and quickselect when everything is in memory and I want the fastest average.
What they are checkingWhether you notice that the cost depends on k, and can change the question when k is large.
A strong answerWhen k is close to n, the heap is nearly as big as the input, so n log k is nearly n log n and the heap gives little. I can flip the question: find the n − k farthest points instead, using a small heap of size n − k, and return everything else. Or I can simply sort, or use quickselect. I would pick whichever has the smaller heap.