-
Notifications
You must be signed in to change notification settings - Fork 273
Nearest Delivery Drops
TIP103 Unit 11 Session 2 (Click for link to problem statements)
A depot sits at the origin (0, 0). Delivery stops are given as [x, y] points in points. You want the k stops closest to the depot by straight-line distance.
Return the k closest points in any order.
def k_closest(points, k):
pass- 💡 Difficulty: Medium
- ⏰ Time to complete: 20-30 mins
- 🛠️ Topics: Heaps, Priority Queues, Top-K Elements, Euclidean Distance
Understand what the interviewer is asking for by using test cases and questions about the problem.
- Established a set (2-3) of test cases to verify their own solution later.
- Established a set (1-2) of edge cases to verify their solution handles complexities.
- Have fully understood the problem and have no clarifying questions.
- Have you verified any Time/Space Constraints for this problem?
-
Q: How do we measure how close a stop is to the depot?
- A: By straight-line (Euclidean) distance from the origin:
sqrt(x^2 + y^2). Since the square root is monotonic, we can compare squared distancesx^2 + y^2instead and skip thesqrtentirely.
- A: By straight-line (Euclidean) distance from the origin:
-
Q: Does the order of the returned points matter?
- A: No. The problem says the
kclosest points can be returned in any order.
- A: No. The problem says the
-
Q: Can we assume
kis valid, and can ties occur?- A: Assume
1 <= k <= len(points). If two stops are equally distant, either may be chosen; any valid set ofkclosest points is acceptable.
- A: Assume
HAPPY CASE
Input: points = [[1, 3], [-2, 2]], k = 1
Output: [[-2, 2]]
Explanation: The distance of [1, 3] from the depot is sqrt(10), while [-2, 2] is sqrt(8). Since sqrt(8) < sqrt(10), the single closest stop is [-2, 2].
Input: points = [[3, 3], [5, -1], [-2, 4]], k = 2
Output: [[3, 3], [-2, 4]]
Explanation: The squared distances are 18, 26, and 20. The two smallest are 18 ([3, 3]) and 20 ([-2, 4]), so those two stops are returned (in any order).
EDGE CASE
Input: points = [[0, 0]], k = 1
Output: [[0, 0]]
Explanation: A stop can sit exactly on the depot; its distance is 0 and it is trivially the closest.
Input: points = [[1, 2], [3, 4]], k = 2
Output: [[1, 2], [3, 4]]
Explanation: When k equals the number of stops, every stop is returned.
Match what this problem looks like to known categories of problems, e.g. Linked List or Dynamic Programming, and strategies or patterns in those categories.
For Top-K Elements Problems, we can consider the following approaches:
-
Max-Heap of Size K: Keep a heap of the
kclosest stops seen so far; whenever the heap grows pastk, evict the farthest stop. Python'sheapqis a min-heap, so we store negated distances to simulate a max-heap. -
Sorting: Sort all stops by distance and take the first
k. Simpler, but does more work than needed (O(N log N)instead ofO(N log k)).
Plan the solution with appropriate visualizations and pseudocode.
General Idea:
Maintain a max-heap holding at most k stops, keyed on distance from the depot. For each stop, compute its squared distance and push it onto the heap; if the heap now holds more than k stops, pop the farthest one. After processing every stop, the heap contains exactly the k closest stops.
1) Initialize an empty heap.
2) For each point [x, y] in points:
a) Compute the squared distance: dist = x^2 + y^2 (no sqrt needed for comparison).
b) Push (-dist, [x, y]) onto the heap. Negating the distance turns Python's min-heap into a max-heap.
c) If the heap size exceeds k, pop from the heap. This removes the entry with the largest distance seen so far.
3) Return the points remaining in the heap.
- Forgetting to negate the distance:
heapqis a min-heap, so popping would evict the closest stop instead of the farthest. - Computing
sqrtfor every point. It is unnecessary for comparisons and introduces floating-point values where integers suffice. - Popping before pushing (or never capping the heap at
k), so the heap ends up with the wrong number of points. - Returning the negated distances (or
(dist, point)tuples) instead of just the points.
Implement the code to solve the algorithm.
import heapq
def k_closest(points, k):
# Max-heap of at most k stops, keyed on negated squared distance
heap = []
for x, y in points:
dist = x * x + y * y # squared distance; sqrt is not needed to compare
heapq.heappush(heap, (-dist, [x, y]))
if len(heap) > k:
heapq.heappop(heap) # evict the farthest stop seen so far
return [point for _, point in heap]Review the code by running specific example(s) and recording values (watchlist) of your code's variables along the way.
-
Input: points = 1, 3], [-2, 2, k = 1
- [1, 3]: dist = 1 + 9 = 10. Push (-10, [1, 3]). Heap size 1, no eviction.
- [-2, 2]: dist = 4 + 4 = 8. Push (-8, [-2, 2]). Heap size 2 > 1, so pop (-10, [1, 3]) — the farthest stop.
- Output: -2, 2
-
Input: points = 3, 3], [5, -1], [-2, 4, k = 2
- [3, 3]: dist = 18. Push (-18, [3, 3]).
- [5, -1]: dist = 26. Push (-26, [5, -1]). Heap size 2, no eviction.
- [-2, 4]: dist = 20. Push (-20, [-2, 4]). Heap size 3 > 2, so pop (-26, [5, -1]) — the farthest stop.
- Output: -2, 4], [3, 3 (equivalent to 3, 3], [-2, 4 since any order is accepted)
Evaluate the performance of your algorithm and state any strong/weak or future potential work.
Assume N is the number of delivery stops and k is the number of stops requested.
-
Time Complexity:
O(N log k)because each of theNstops triggers at most one push and one pop on a heap that never exceedsk + 1elements. -
Space Complexity:
O(k)for the heap holding the closest stops (ignoring theO(k)output list).