Leetcode973. K Closest Points to Origin

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

Approach1. PriorityQueue - maxHeap

When we get this question, we definitely want a data structure to store things as close as possible to us, and so there's gonna sound a little counterintuitive, but I think the way we are going to do, this is we are going to use a max-heap. Okay and so we are gonna use a max-heap and we are gonna make sure that our heap is always of size K or at most K, and so every single time you add something to the heap and it's greater than K, we are just gonna remove the next thing in the heap, and so what that will do is it will take the maximum or the largest or furthest point from the origin, and it's gonna kick it out.

Awesome, so that's how we are gonna do this, we are actually going to create a heap keeping it at most of size K, and we are gonna organize our heap by the largest distance away from the origin. So then once we basically inserted all of our points removing anything from the root of the heap every time, it gets greater than K when our loop terminates, we will basically have the K smallest points or K closest points to the origin.

So, Let's go ahead and do that, we are gonna have a priority queue, and it's going to hold Integer arrays, we are going call this maxHeap equals new Priority queue and we are gonna write a comparator here, we gonna say a comma b meaning two points, and so now this is like where the math kind of comes in but we are basically just calculating the Euclidean distance, so don't worry about this. So now we are going to do is we are just going to throw all of our points in the heap. Next, using for loop, every point in our points, we are going to add it to the heap. now we are gonna check right, so if max-heap.size() is greater than K, then we need to remove something, so we're gonna remove from the heap, we're gonna remove from the root. So we are actually removing the point that is a farthest away right now.

Instead, we can maintain a max-heap with size K. Then for each point, we add it to the heap. Once the size of the heap is greater than K, we are supposed to extract one from the max heap to ensure the size of the heap is always K. Thus, the max heap is always maintained top K smallest elements from the first one to the current one. Once the size of the heap is over its maximum capacity, it will exclude the maximum element in it, since it can not be the proper candidate anymore.

Theoretically, the time complexity is O(NlogK),

The advantage of this solution is it can deal with real-time(online) stream data. It does not have to know the size of the data previously.
The disadvantage of this solution is it is not the most efficient solution.

The shortcode shows as follows:

class Solution {
     public int[][] kClosest(int[][] points, int K) {
          if(points == null || points.length == 0 || K < 1){
              return points;
         }
         PriorityQueue pq = new PriorityQueue((a, b) -> getDistanceSquare(b) - getDistanceSquare(a));
         
         for(int[] point : points){
             pq.add(point);
             if(pq.size() > K){
                 pq.poll();
             }
         }
         int [][] res = new int[K][2];
         for(int i = 0; i < K; i++){
             res[i] = pq.poll();
         }
         return res;
     }
     
     private int getDistanceSquare(int [] point){
         return point[0]*point[0] + point[1]*point[1];
     }
}

用maxHeap来维护K shortest distence.
Time Complexity: O(nlogK). n = points.length.
Space: O(n).

Approach2. Quick sort

The last solution is based on quick sort, we can also call it quick select. In the quick sort, we will always choose a pivot to compare with other elements. After one iteration, we will get an array that all elements smaller than the pivot are on the left side of the pivot and all elements greater than the pivot are on the right side of the pviot (assuming we sort the array in ascending order). So, inspired from this, each iteration, we choose a pivot and then find the position p the pivot should be. Then we compare p with the K, if the p is smaller than the K, meaning the all element on the left of the pivot are all proper candidates but it is not adequate, we have to do the same thing on right side, and vice versa. If the p is exactly equal to the K, meaning that we've found the K-th position. Therefore, we just return the first K elements, since they are not greater than the pivot.

Theoretically, the average time complexity is O(N) , but just like quick sort, in the worst case, this solution would be degenerated to O(N^2), and pratically, the real time it takes on leetcode is 15ms.

The advantage of this solution is it is very efficient.
The disadvatage of this solution are it is neither an online solution nor a stable one. And the K elements closest are not sorted in ascending order.

The short code shows as follows:

class Solution {
    public int[][] kClosest(int[][] points, int K) {
        if(points == null || points.length == 0 || K < 1) {
            return points;
        }
        int left = 0, right = points.length - 1;
        while(left <= right) {
            
            int mid = helper(points, left, right);
            if(mid == K) break;
            if(mid < K) {
                left = mid + 1;
            }else {
                right = mid - 1;
            }
        }
        return Arrays.copyOfRange(points, 0, K);
    }
    
    private int helper(int[][] points, int left, int right) {
        int[] pivot = points[left];
        while(left < right){
            while(left < right && compare(points[right], pivot) >= 0) right--;
            points[left] = points[right];
            while(left < right && compare(points[left], pivot) <= 0) left++;
            points[right] = points[left];
        }
        points[left] = pivot;
        return left;
    }
    private int compare(int[] A, int[] B) {
        return A[0] * A[0] + A[1] * A[1] - B[0] * B[0] - B[1] * B[1];
    }
}

"Because in the quicksort, you have to take care of two sides of the pivot. But in quickselect, you only focus on the side the target object should be. So, in optimal case, the running time of quicksort is (n + 2 * (n/2) + 4 * (n/4)...), it has logn iterations. Instead, the running time of quickselect would be (n + n/2 + n/4 +...) it has logn iterations as well. Therefore, the running time of quicksort is O(nlogn), instead, the running time of quickselect of quickselect is O(n)
参考:https://leetcode.com/problems/k-closest-points-to-origin/discuss/220235/Java-Three-solutions-to-this-classical-K-th-problem.

你可能感兴趣的:(Leetcode973. K Closest Points to Origin)