
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
k
smallest distances. This can be achieved by:- Sorting the list of distances and then selecting the first
k
elements. - Using a max-heap (or priority queue) of size
k
to maintain thek
smallest 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
k
relative to the length of thepoints
list:- If
k
is considerably smaller than the number of points, a heap is more efficient as it maintains a smaller subset of points in memory. - If
k
is relatively large, sorting the whole array and then slicing out the firstk
elements 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=1
is[-2,2]
. - In Example 2, selecting
k=2
closest 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 initializesstart
andend
pointers at the beginning and the end of thepoints
array. It uses these pointers to refine the search space in each iteration based on the results from thedivide
function. 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 inpoints
so 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, helpingperformQuickSelect
adjust 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
kNearest
method is the entry point that accepts thepoints
array and the integerk
. It delegates the task to a helper methodselectKClosest
.selectKClosest
initializes two pointers (lower
andupper
) to manage the range of points to be processed and organizes the closestk
points 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
partitionPoints
method works by selecting a pivot using theselectPivot
method, arranging the points such that those with a distance less than the pivot's distance are moved to the left.computeDistance
is 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. performQuickselect
initializes boundaries (l
for left andr
for right) used to repeatedly partition the points array until the partition indexidx
matchesk
.selectPartition
assists 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
points
is repeatedly partitioned: points closer to the origin than the pivot come before it, others after. - The loop in
performQuickSelect
adjusts the values ofl
andr
based onidx
andk
, effectively narrowing down the segment of the array to consider. - Finally, the first
k
elements of the array, now the closestk
points, 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
points
and an integerk
. It initializes the process by callingquickSelect
with the same parameters. - quickSelect(points, k): Performs the Quickselect algorithm to find the
k
closest 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
start
andend
in 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
start
andend
) 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.
No comments yet.