Skip to content

Commit 851dcb5

Browse files
vinceajcsgoswami-rahul
authored andcommitted
Add k closest points to origin (keon#435)
* Add k closest points to origin * Update heap init file * Add tests for k closest points to origin * Add k closest points link to README * Update k closest points to origin Co-Authored-By: vinceajcs <[email protected]>
1 parent 2a664a8 commit 851dcb5

File tree

4 files changed

+68
-8
lines changed

4 files changed

+68
-8
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ If you want to uninstall algorithms, it is as simple as:
158158
- [skyline](algorithms/heap/skyline.py)
159159
- [sliding_window_max](algorithms/heap/sliding_window_max.py)
160160
- [binary_heap](algorithms/heap/binary_heap.py)
161+
- [k_closest_points](algorithms/heap/k_closest_points.py)
161162
- [linkedlist](algorithms/linkedlist)
162163
- [add_two_numbers](algorithms/linkedlist/add_two_numbers.py)
163164
- [copy_random_pointer](algorithms/linkedlist/copy_random_pointer.py)

algorithms/heap/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
from .binary_heap import *
22
from .skyline import *
33
from .sliding_window_max import *
4+
from .merge_sorted_k_lists import *
5+
from .k_closest_points import *

algorithms/heap/k_closest_points.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""Given a list of points, find the k closest to the origin.
2+
3+
Idea: Maintain a max heap of k elements.
4+
We can iterate through all points.
5+
If a point p has a smaller distance to the origin than the top element of a heap, we add point p to the heap and remove the top element.
6+
After iterating through all points, our heap contains the k closest points to the origin.
7+
"""
8+
9+
10+
from heapq import heapify, heappushpop
11+
12+
13+
def k_closest(points, k, origin=(0, 0)):
14+
# Time: O(k+(n-k)logk)
15+
# Space: O(k)
16+
"""Initialize max heap with first k points.
17+
Python does not support a max heap; thus we can use the default min heap where the keys (distance) are negated.
18+
"""
19+
heap = [(-distance(p, origin), p) for p in points[:k]]
20+
heapify(heap)
21+
22+
"""
23+
For every point p in points[k:],
24+
check if p is smaller than the root of the max heap;
25+
if it is, add p to heap and remove root. Reheapify.
26+
"""
27+
for p in points[k:]:
28+
d = distance(p, origin)
29+
30+
heappushpop(heap, (-d, p)) # heappushpop does conditional check
31+
"""Same as:
32+
if d < -heap[0][0]:
33+
heappush(heap, (-d,p))
34+
heappop(heap)
35+
36+
Note: heappushpop is more efficient than separate push and pop calls.
37+
Each heappushpop call takes O(logk) time.
38+
"""
39+
40+
return [p for nd, p in heap] # return points in heap
41+
42+
43+
def distance(point, origin=(0, 0)):
44+
return (point[0] - origin[0])**2 + (point[1] - origin[1])**2

tests/test_heap.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
from algorithms.heap import (
22
BinaryHeap,
33
get_skyline,
4-
max_sliding_window
4+
max_sliding_window,
5+
k_closest_points
56
)
67

78
import unittest
89

10+
911
class TestBinaryHeap(unittest.TestCase):
1012
"""
1113
Test suite for the binary_heap data structures
1214
"""
15+
1316
def setUp(self):
1417
self.min_heap = BinaryHeap()
1518
self.min_heap.insert(4)
@@ -24,31 +27,41 @@ def test_insert(self):
2427
# After insert: [0, 2, 50, 4, 55, 90, 87, 7]
2528
self.min_heap.insert(2)
2629
self.assertEqual([0, 2, 50, 4, 55, 90, 87, 7],
27-
self.min_heap.heap)
30+
self.min_heap.heap)
2831
self.assertEqual(7, self.min_heap.currentSize)
2932

3033
def test_remove_min(self):
3134
ret = self.min_heap.remove_min()
3235
# Before remove_min : [0, 4, 50, 7, 55, 90, 87]
3336
# After remove_min: [7, 50, 87, 55, 90]
3437
# Test return value
35-
self.assertEqual(4,ret)
38+
self.assertEqual(4, ret)
3639
self.assertEqual([0, 7, 50, 87, 55, 90],
37-
self.min_heap.heap)
40+
self.min_heap.heap)
3841
self.assertEqual(5, self.min_heap.currentSize)
3942

43+
4044
class TestSuite(unittest.TestCase):
4145
def test_get_skyline(self):
42-
buildings = [ [2, 9, 10], [3, 7, 15], [5, 12, 12], \
43-
[15, 20, 10], [19, 24, 8] ]
46+
buildings = [[2, 9, 10], [3, 7, 15], [5, 12, 12],
47+
[15, 20, 10], [19, 24, 8]]
4448
# Expect output
45-
output = [ [2, 10], [3, 15], [7, 12], [12, 0], [15, 10], \
46-
[20, 8], [24, 0] ]
49+
output = [[2, 10], [3, 15], [7, 12], [12, 0], [15, 10],
50+
[20, 8], [24, 0]]
4751
self.assertEqual(output, get_skyline(buildings))
4852

4953
def test_max_sliding_window(self):
5054
nums = [1, 3, -1, -3, 5, 3, 6, 7]
5155
self.assertEqual([3, 3, 5, 5, 6, 7], max_sliding_window(nums, 3))
5256

57+
def test_k_closest_points(self):
58+
points = [(1, 0), (2, 3), (5, 2), (1, 1), (2, 8), (10, 2), (-1, 0), (-2, -2)]
59+
self.assertEqual([(-1, 0), (1, 0)], k_closest(points, 2))
60+
self.assertEqual([(1, 1), (-1, 0), (1, 0)], k_closest(points, 3))
61+
self.assertEqual([(-2, -2), (1, 1), (1, 0), (-1, 0)], k_closest(points, 4))
62+
self.assertEqual([(10, 2), (2, 8), (5, 2), (-2, -2), (2, 3),
63+
(1, 0), (-1, 0), (1, 1)], k_closest(points, 8))
64+
65+
5366
if __name__ == "__main__":
5467
unittest.main()

0 commit comments

Comments
 (0)