
Problem Statement
Given an array named points, where each element points[i] = [xi, yi] represents coordinates of a point on a two-dimensional, X-Y plane, the task is to determine k points that are closest to the origin (0, 0). The measure of closeness is defined by the Euclidean distance, calculated as √((x1 - x2)^2 + (y1 - y2)^2). The result should include the k closest points and can be presented in any order. The uniqueness of the answer refers to the distances being definitively smaller than those of points not included, rather than the order of points themselves which are considered.
Examples
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]]
Explanation:
The answer [[-2,4],[3,3]] would also be accepted.
Constraints
1 <= k <= points.length <= 104-104 <= xi, yi <= 104
Approach and Intuition
To find the k closest points to the origin, the intuitive approach involves:
Calculate the distance of each point from the origin using the formula for Euclidean distance. Here, for simpler computation and efficiency, we often use squared distance, i.e.,
(x^2 + y^2)because squaring and square roots are computationally expensive and unnecessary for just comparing distances.Store these distances along with their respective point coordinates, perhaps in a tuple or a list for easy sorting or selection.
Once we have all distances, we need a method to select the
ksmallest distances. This can be achieved by:- Sorting the list of distances and then selecting the first
kelements. - Using a max-heap (or priority queue) of size
kto maintain theksmallest distances encountered so far.
- Sorting the list of distances and then selecting the first
The choice between sorting and using a heap depends on the value of
krelative to the length of thepointslist:- If
kis considerably smaller than the number of points, a heap is more efficient as it maintains a smaller subset of points in memory. - If
kis relatively large, sorting the whole array and then slicing out the firstkelements might be simpler and just as efficient.
- If
From example descriptions:
- For instance, in the given Example 1, with
points = [[1,3],[-2,2]]andk = 1, calculating distances we find that[-2,2]has the smaller distance from the origin compared to[1,3]. Thus, the closest point to the origin based on the requirementk=1is[-2,2]. - In Example 2, selecting
k=2closest points from[[3,3],[5,-1],[-2,4]], both[3,3]and[-2,4]have smaller distances compared to[5,-1]making them the closest points as per the requirement.
- For instance, in the given Example 1, with
Remember, in programming solutions using most languages and libraries, the methods for leveraging heaps or sorting could slightly differ in syntax but follow this intuitive approach for an optimal and clear implementation.
Solutions
- C++
- Java
- JavaScript
- Python
class Solution {
public:
vector<vector<int>> findKClosest(vector<vector<int>>& points, int k) {
return performQuickSelect(points, k);
}
private:
vector<vector<int>> performQuickSelect(vector<vector<int>>& points, int k) {
int start = 0, end = points.size() - 1;
int pivotIdx = points.size();
while (pivotIdx != k) {
pivotIdx = divide(points, start, end);
if (pivotIdx < k) {
start = pivotIdx;
} else {
end = pivotIdx - 1;
}
}
return vector<vector<int>>(points.begin(), points.begin() + k);
};
int divide(vector<vector<int>>& points, int start, int end) {
vector<int>& pivotElement = pickPivot(points, start, end);
int pivotDistance = calculateDistance(pivotElement);
while (start < end) {
if (calculateDistance(points[start]) >= pivotDistance) {
points[start].swap(points[end]);
end--;
} else {
start++;
}
}
if (calculateDistance(points[start]) < pivotDistance)
start++;
return start;
};
vector<int>& pickPivot(vector<vector<int>>& points, int start, int end) {
return points[start + (end - start) / 2];
}
int calculateDistance(vector<int>& point) {
return point[0] * point[0] + point[1] * point[1];
}
};
The provided C++ code defines a solution for finding the k closest points to the origin from a list of points in a 2D plane using a modified quickselect algorithm.
The Solution class contains three primary functions apart from findKClosest:
performQuickSelect: This function manages the quick select process. It initializesstartandendpointers at the beginning and the end of thepointsarray. It uses these pointers to refine the search space in each iteration based on the results from thedividefunction. This continues until the pivot index is equal to k, ensuring that the first k elements of the array are the ones closest to the origin.divide: This function operates similarly to the partition function in quicksort. It selects a pivot usingpickPivot, calculates its distance from the origin, and rearranges elements inpointsso that elements less than the pivot come before it and all elements greater come after it. The function returns the final position of the pivot, helpingperformQuickSelectadjust its search range.pickPivot: It selects a middle element as the pivot to be used individe. This choice can mitigate the effects of a poorly chosen pivot, such as might happen with sorted or nearly sorted input data.calculateDistance: A helper method that computes the squared Euclidean distance from the origin for a given point, which avoids the computational cost of square roots and is sufficient for comparison purposes.
The working process involves:
- Choosing a pivot and dividing the points into segments more and less distant than the pivot.
- Iteratively adjusting the pivot position until the k smallest elements are isolated.
- Returning the result as the required list of k closest points.
The algorithm emphasizes efficiency by focusing only on identifying the k smallest elements rather than fully sorting the data, which is typically more resource-intensive. This method is particularly suitable for large datasets where finding the exact order of all elements is unnecessary.
class Solution {
public int[][] kNearest(int[][] points, int k) {
return selectKClosest(points, k);
}
private int[][] selectKClosest(int[][] points, int k) {
int lower = 0, upper = points.length - 1;
int pivotIdx = points.length;
while (pivotIdx != k) {
pivotIdx = partitionPoints(points, lower, upper);
if (pivotIdx < k) {
lower = pivotIdx;
} else {
upper = pivotIdx - 1;
}
}
return Arrays.copyOf(points, k);
}
private int partitionPoints(int[][] points, int lower, int upper) {
int[] pivotPoint = selectPivot(points, lower, upper);
int pivotDistance = computeDistance(pivotPoint);
while (lower < upper) {
if (computeDistance(points[lower]) >= pivotDistance) {
int[] temp = points[lower];
points[lower] = points[upper];
points[upper] = temp;
upper--;
} else {
lower++;
}
}
if (computeDistance(points[lower]) < pivotDistance)
lower++;
return lower;
}
private int[] selectPivot(int[][] points, int lower, int upper) {
return points[lower + (upper - lower) / 2];
}
private int computeDistance(int[] point) {
return point[0] * point[0] + point[1] * point[1];
}
}
This Java solution tackles the problem of finding the k closest points to the origin in a given list of points that contains coordinates in a 2D plane. The approach employs the quickselect algorithm to efficiently solve the issue by partially sorting the points until the k closest are grouped at the beginning of the list.
To understand how this solution effectively achieves the goal:
The
kNearestmethod is the entry point that accepts thepointsarray and the integerk. It delegates the task to a helper methodselectKClosest.selectKClosestinitializes two pointers (lowerandupper) to manage the range of points to be processed and organizes the closestkpoints towards the beginning of the array. This is done through a partitioning process in which elements are rearranged based on their distance from the origin.The
partitionPointsmethod works by selecting a pivot using theselectPivotmethod, arranging the points such that those with a distance less than the pivot's distance are moved to the left.computeDistanceis a utility function to calculate the squared distance of a point from the origin, thus avoiding the computational expense of square root calculations.
At the completion of the process, the top k elements in the array are the desired closest points to the origin. These are returned after ensuring that the subsection of the points array containing the closest k points is separated out using Arrays.copyOf.
Ensure you appropriately choose the value of k to avoid bounds errors, as k must not exceed the length of the points array. This method efficiently handles the problem with a complexity that's generally better than sorting the entire array, making it suitable for large datasets.
var nearestKPoints = function(points, k) {
return performQuickSelect(points, k)
};
var performQuickSelect = function(points, k) {
let l = 0, r = points.length - 1
let idx = points.length
while (idx !== k) {
// Applying partitioning on the points to find the k closest
idx = selectPartition(points, l, r)
if (idx < k) {
l = idx
} else {
r = idx - 1
}
}
// Extract the first k points from the array
return points.slice(0, k)
};
var selectPartition = function(points, l, r) {
let pivot = selectPivot(points, l, r)
let dist = distanceSquared(pivot)
while (l < r) {
if (distanceSquared(points[l]) >= dist) {
[points[l], points[r]] = [points[r], points[l]]
r--
} else {
l++
}
}
if (distanceSquared(points[l]) < dist)
l++
return l
};
const selectPivot = (points, l, r) => points[l + ((r - l) >> 1)]
const distanceSquared = ([x, y]) => x * x + y * y
The given JavaScript function nearestKPoints inputs an array of points and an integer k, then returns the k closest points to the origin (0, 0), utilizing the Quickselect algorithm.
- Process starts by invoking
performQuickSelect, which is where the Quickselect algorithm is implemented. performQuickselectinitializes boundaries (lfor left andrfor right) used to repeatedly partition the points array until the partition indexidxmatchesk.selectPartitionassists in this by choosing a pivot point and reordering the elements based on their squared distance from the origin; this distance is computed indistanceSquared.- The array
pointsis repeatedly partitioned: points closer to the origin than the pivot come before it, others after. - The loop in
performQuickSelectadjusts the values oflandrbased onidxandk, effectively narrowing down the segment of the array to consider. - Finally, the first
kelements of the array, now the closestkpoints, are returned using.slice(0, k).
This method efficiently handles the selection of nearest points using spatial comparisons and minimizes the need to sort the entire array, thus optimizing performance especially for large datasets.
class Solver:
def nearestKPoints(self, points: List[List[int]], k: int) -> List[List[int]]:
return self.quickSelect(points, k)
def quickSelect(self, points: List[List[int]], k: int) -> List[List[int]]:
start, end = 0, len(points) - 1
index = len(points)
while index != k:
index = self.partitionArray(points, start, end)
if index < k:
start = index
else:
end = index - 1
return points[:k]
def partitionArray(self, points: List[List[int]], start: int, end: int) -> int:
pivotPoint = self.getMidpoint(points, start, end)
pivotDist = self.calculateDist(pivotPoint)
while start < end:
if self.calculateDist(points[start]) >= pivotDist:
points[start], points[end] = points[end], points[start]
end -= 1
else:
start += 1
if self.calculateDist(points[start]) < pivotDist:
start += 1
return start
def getMidpoint(self, points: List[List[int]], start: int, end: int) -> List[int]:
return points[start + (end - start) // 2]
def calculateDist(self, point: List[int]) -> int:
return point[0]**2 + point[1]**2
To identify the k closest points to the origin using your given set of points, the provided Python solution employs a class named Solver which integrates a Quickselect algorithm. Here’s a straightforward breakdown of how this implementation works:
Method Definitions
- nearestKPoints(points, k): This is the entry method that takes a list of
pointsand an integerk. It initializes the process by callingquickSelectwith the same parameters. - quickSelect(points, k): Performs the Quickselect algorithm to find the
kclosest points. It works by partitioning the points list around a pivot so that points nearer to the origin are on one side of the pivot. The process repeats on the relevant partition until the partition size matchesk. - partitionArray(points, start, end): Arranges the elements in the list such that those closer to the origin than the pivot are moved to the front of the list. This support method returns the new pivot index after partitioning.
- getMidpoint(points, start, end): Selects the midpoint between
startandendin the list as the pivot point. - calculateDist(point): Computes the distance of a point from the origin which is used to rank points in relation to the pivot.
- nearestKPoints(points, k): This is the entry method that takes a list of
Functionality
- Begin by selecting the pivot using the midpoint of the list segment.
- Calculate the Euclidean distance for the pivot and employ this distance to position other points relative to the pivot using
partitionArray. - Continue refining the range (using
startandend) and reselecting pivots until the section of points of interest narrows down to exactlyk.
This solution is efficient, leveraging the O(n) average time complexity of the Quickselect algorithm, making it suitable for large datasets where sorting the entire list would be too costly.