K Closest Points to Origin — Leetcode Medium
973. K Closest Points to Origin
Medium : https://leetcode.com/problems/k-closest-points-to-origin/solution/
We have a list of points
on the plane. Find the K
closest points to the origin (0, 0)
.
(Here, the distance between two points on a plane is the Euclidean distance.)
You may return the answer in any order. The answer is guaranteed to be unique (except for the order that it is in.)
Example 1:
Input: points = [[1,3],[-2,2]], K = 1
Output: [[-2,2]]
Explanation:
The distance between (1, 3) and the origin is sqrt(10).
The distance between (-2, 2) and the origin is sqrt(8).
Since sqrt(8) < sqrt(10), (-2, 2) is closer to the origin.
We only want the closest K = 1 points from the origin, so the answer is just [[-2,2]].
Example 2:
Input: points = [[3,3],[5,-1],[-2,4]], K = 2
Output: [[3,3],[-2,4]]
(The answer [[-2,4],[3,3]] would also be accepted.)
Note:
1 <= K <= points.length <= 10000
-10000 < points[i][0] < 10000
-10000 < points[i][1] < 10000
Solution :
Best Case QuickSelect
Approach 2: Divide and Conquer
Intuition
We want an algorithm faster than NlogNNlogN. Clearly, the only way to do this is to use the fact that the K elements returned can be in any order — otherwise we would be sorting which is at least NlogNNlogN.
Say we choose some random element x = A[i]
and split the array into two buckets: one bucket of all the elements less than x
, and another bucket of all the elements greater than or equal to x
. This is known as "quickselecting by a pivot x
".
The idea is that if we quickselect by some pivot, on average in linear time we’ll reduce the problem to a problem of half the size.
Algorithm
Let’s do the work(i, j, K)
of partially sorting the subarray (points[i], points[i+1], ..., points[j])
so that the smallest K
elements of this subarray occur in the first K
positions (i, i+1, ..., i+K-1)
.
First, we quickselect by a random pivot element from the subarray. To do this in place, we have two pointers i
and j
, and move these pointers to the elements that are in the wrong bucket -- then, we swap these elements.
After, we have two buckets [oi, i]
and [i+1, oj]
, where (oi, oj)
are the original (i, j)
values when calling work(i, j, K)
. Say the first bucket has 10
items and the second bucket has 15
items. If we were trying to partially sort say, K = 5
items, then we only need to partially sort the first bucket: work(oi, i, 5)
. Otherwise, if we were trying to partially sort say, K = 17
items, then the first 10
items are already partially sorted, and we only need to partially sort the next 7 items: work(i+1, oj, 7)
.
Solution:
We implement partition and then we also implement the Sort()
def kClosest(self, points, K): dist = lambda i: points[i][0]**2 + points[i][1]**2 # Quick sort partitioning using Horre's partitioning
def partition(i, j):
# Partition by pivot A[i], returning an index mid
# such that A[i] <= A[mid] <= A[j] for i < mid < j.
pi = i
pivot = dist(i)
i += 1 while True:
while i < j and dist(i) < pivot:
i += 1
while i <= j and dist(j) >= pivot:
j -= 1
if i >= j: break # swap where the dist(i) > pivot and dist(j) < pivot
points[i], points[j] = points[j], points[i]# Myself: Now all points upto the jth are smaller than pivot
# move the pivot to j and return pivot's index which is j.
points[pi], points[j] = points[j], points[pi]
return j
def sort(i, j, K):
# Partially sorts A[i:j+1] so the first K elements are
# the smallest K elements.
if i >= j: return # Put random element as A[i] - this is the pivot
k = random.randint(i, j)
points[i], points[k] = points[k], points[i] pi = partition(i, j) if K < pi - i + 1: sort(i, pi - 1, K) elif K > pi - i + 1:
# Notice how we reduced K to K - (pi - i +1)
sort(pi + 1, j, K - (pi - i + 1)) sort(0, len(points) - 1, K) # our purpose is the ensure all k closest elements are
# included in the first K positions. So We do QuickSelect till out pivot is at the Kth position. Then we quit. return points[:K]
Complexity Analysis
- Time Complexity: O(N) in average case and O(N2) in the worst case, where N is the length of
points
. - Space Complexity: O(N).
Other approach:
Approach 1: Sort
Intuition
Sort the points by distance, then take the closest K points.
Algorithm:
In Python, we sort by a custom key function — namely, the distance to the origin. Afterwards, we return the first K elements of the list.
class Solution(object):
def kClosest(self, points, K):
points.sort(key = lambda P: P[0]**2 + P[1]**2)
return points[:K]
Complexity Analysis
- Time Complexity: O(NlogN), where N is the length of
points
. - Space Complexity: O(N).