973. K Closest Points to Origin
Problem Description
You are given an array of points on a 2D plane where each point is represented as [x, y]
coordinates. Your task is to find the k
closest points to the origin (0, 0)
.
The distance between any point and the origin is calculated using the Euclidean distance formula: β(xΒ² + yΒ²)
, where x
and y
are the coordinates of the point.
For example, if you have points [[1,3], [-2,2], [5,8]]
and k = 2
, you need to:
- Calculate the distance of each point from the origin:
- Point
[1,3]
: distance =β(1Β² + 3Β²) = β10 β 3.16
- Point
[-2,2]
: distance =β(4 + 4) = β8 β 2.83
- Point
[5,8]
: distance =β(25 + 64) = β89 β 9.43
- Point
- Select the 2 closest points:
[[-2,2], [1,3]]
The solution sorts all points based on their distance from the origin using the hypot
function (which calculates β(xΒ² + yΒ²)
), then returns the first k
points from the sorted list. The points can be returned in any order since the problem guarantees that the answer is unique except for the ordering.
Intuition
The key insight is that we need to identify which points are closest to the origin. Since we want the k
closest points, we need a way to compare distances and select the smallest ones.
The most straightforward approach is to think of this as a selection problem: if we can order all points by their distance from the origin, then the first k
points in this ordering will be our answer. This naturally leads us to a sorting-based solution.
Why does sorting work here? When we sort the points by their distance from the origin (from smallest to largest), we create an ordered list where:
- The 1st point is the closest to the origin
- The 2nd point is the second closest
- And so on...
Once we have this sorted order, getting the k
closest points becomes trivial - we just take the first k
elements from our sorted array.
The mathematical foundation is that the Euclidean distance β(xΒ² + yΒ²)
gives us a single numerical value for each point that represents how far it is from the origin. These distance values can be directly compared and sorted. Python's hypot(x, y)
function conveniently calculates this distance for us.
While there are more advanced approaches using heaps that could be more efficient for very large datasets with small k
, the sorting approach is clean, simple to understand, and works well for most cases with a time complexity of O(n log n)
.
Learn more about Math, Divide and Conquer, Sorting and Heap (Priority Queue) patterns.
Solution Approach
The solution uses a custom sorting approach to find the k
closest points to the origin. Let's walk through the implementation step by step:
Step 1: Sort the Points Array
The core of the solution is the sorting operation:
points.sort(key=lambda p: hypot(p[0], p[1]))
This line does several things:
- It sorts the
points
array in-place - Uses a lambda function as the sorting key:
lambda p: hypot(p[0], p[1])
- For each point
p = [x, y]
, the lambda extractsp[0]
(x-coordinate) andp[1]
(y-coordinate) - The
hypot(x, y)
function calculatesβ(xΒ² + yΒ²)
, which is the Euclidean distance from the origin
Step 2: Return the First k Points
After sorting, the points are arranged in ascending order of their distance from the origin:
return points[:k]
This slice operation points[:k]
returns a new list containing the first k
elements from the sorted array, which are exactly the k
closest points to the origin.
Algorithm Analysis:
- Data Structure: The solution works directly with the input array, no additional data structures are needed
- Pattern: This follows the "sort and select" pattern, where we sort based on a custom criterion and then select the required elements
- Time Complexity:
O(n log n)
wheren
is the number of points, dominated by the sorting operation - Space Complexity:
O(1)
if we don't count the space used by the sorting algorithm's internal operations (Python's Timsort usesO(n)
space in worst case)
The beauty of this approach lies in its simplicity - by leveraging Python's built-in sorting with a custom key function, we can solve the problem in just two lines of code while maintaining clarity and efficiency.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example with points = [[3,3], [5,-1], [-2,4]]
and k = 2
.
Step 1: Calculate distances for sorting
For each point, we need to calculate its distance from the origin using hypot(x, y)
:
- Point
[3,3]
:hypot(3, 3) = β(9 + 9) = β18 β 4.24
- Point
[5,-1]
:hypot(5, -1) = β(25 + 1) = β26 β 5.10
- Point
[-2,4]
:hypot(-2, 4) = β(4 + 16) = β20 β 4.47
Step 2: Sort points by distance
When we execute points.sort(key=lambda p: hypot(p[0], p[1]))
, the sorting algorithm:
- Applies the lambda function to each point to get its distance
- Sorts the points based on these distance values in ascending order
After sorting, our array becomes:
- Position 0:
[3,3]
with distance β 4.24 (smallest) - Position 1:
[-2,4]
with distance β 4.47 (middle) - Position 2:
[5,-1]
with distance β 5.10 (largest)
So points = [[3,3], [-2,4], [5,-1]]
after sorting.
Step 3: Select the first k points
Since k = 2
, we take the first 2 elements using points[:2]
:
- This gives us
[[3,3], [-2,4]]
These are indeed the 2 closest points to the origin from our original set.
Verification:
[3,3]
is at distance β18 from origin[-2,4]
is at distance β20 from origin[5,-1]
is at distance β26 from origin
The first two have the smallest distances, confirming our answer is correct.
Solution Implementation
1from typing import List
2from math import hypot
3
4class Solution:
5 def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
6 # Sort points by their Euclidean distance from origin (0, 0)
7 # hypot(x, y) calculates sqrt(x^2 + y^2) efficiently
8 points.sort(key=lambda point: hypot(point[0], point[1]))
9
10 # Return the first k closest points
11 return points[:k]
12
1class Solution {
2 /**
3 * Find the k closest points to the origin (0, 0) in a 2D plane.
4 *
5 * @param points Array of points where each point is represented as [x, y]
6 * @param k Number of closest points to return
7 * @return Array containing the k closest points to the origin
8 */
9 public int[][] kClosest(int[][] points, int k) {
10 // Sort points based on their Euclidean distance from the origin
11 // Using Math.hypot to calculate the distance: sqrt(x^2 + y^2)
12 Arrays.sort(points, (point1, point2) -> {
13 double distance1 = Math.hypot(point1[0], point1[1]);
14 double distance2 = Math.hypot(point2[0], point2[1]);
15
16 // Compare distances and return appropriate comparison result
17 if (distance1 - distance2 > 0) {
18 return 1; // point1 is farther from origin
19 } else {
20 return -1; // point1 is closer or equal distance from origin
21 }
22 });
23
24 // Return the first k points after sorting
25 return Arrays.copyOfRange(points, 0, k);
26 }
27}
28
1class Solution {
2public:
3 vector<vector<int>> kClosest(vector<vector<int>>& points, int k) {
4 // Sort all points by their Euclidean distance from origin (0, 0)
5 // Using lambda function to compare distances
6 sort(points.begin(), points.end(), [](const vector<int>& point1, const vector<int>& point2) {
7 // Calculate Euclidean distance using hypot function
8 // hypot(x, y) computes sqrt(x^2 + y^2) efficiently
9 double distance1 = hypot(point1[0], point1[1]);
10 double distance2 = hypot(point2[0], point2[1]);
11
12 // Return true if point1 is closer to origin than point2
13 return distance1 < distance2;
14 });
15
16 // Return the first k points after sorting
17 // Create a new vector containing elements from begin to begin+k
18 return vector<vector<int>>(points.begin(), points.begin() + k);
19 }
20};
21
1/**
2 * Finds the k closest points to the origin (0, 0) in a 2D plane
3 * @param points - Array of points where each point is represented as [x, y]
4 * @param k - Number of closest points to return
5 * @returns Array of k closest points to the origin
6 */
7function kClosest(points: number[][], k: number): number[][] {
8 // Sort points by their Euclidean distance from the origin
9 // Math.hypot calculates the square root of the sum of squares (distance formula)
10 points.sort((pointA: number[], pointB: number[]) => {
11 const distanceA: number = Math.hypot(pointA[0], pointA[1]);
12 const distanceB: number = Math.hypot(pointB[0], pointB[1]);
13 return distanceA - distanceB;
14 });
15
16 // Return the first k points after sorting
17 return points.slice(0, k);
18}
19
Time and Space Complexity
The time complexity is O(n log n)
, where n
is the length of the array points
. This is because the code uses Python's built-in sort()
method, which implements Timsort algorithm with O(n log n)
time complexity in the average and worst cases. Each distance calculation using hypot()
takes O(1)
time, and since it's called for each element during sorting comparisons, the overall time complexity remains O(n log n)
.
The space complexity is O(log n)
. While Python's sort()
method sorts the list in-place (not creating a new list), it still requires O(log n)
space for the recursion stack used internally by the Timsort algorithm. The hypot()
function and the lambda function use O(1)
additional space. The return statement points[:k]
creates a new list of size k
, but since k β€ n
, and the dominant space factor is the sorting algorithm's recursion stack, the overall space complexity is O(log n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Unnecessary Square Root Calculation
One common pitfall is computing the actual Euclidean distance when it's not necessary. Since we only need to compare distances (not the actual values), we can avoid the expensive square root operation:
Inefficient approach:
points.sort(key=lambda p: hypot(p[0], p[1])) # Calculates sqrt(xΒ² + yΒ²)
Optimized approach:
points.sort(key=lambda p: p[0]**2 + p[1]**2) # Just xΒ² + yΒ²
Since the square root function is monotonic for non-negative values, sorting by squared distances gives the same ordering as sorting by actual distances, but it's computationally cheaper.
2. Modifying the Original Input Array
The current solution modifies the input array in-place with points.sort()
. This can be problematic if:
- The caller expects the original array to remain unchanged
- You need to preserve the original order for other operations
Solution:
def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
# Create a copy to avoid modifying the original
sorted_points = sorted(points, key=lambda p: p[0]**2 + p[1]**2)
return sorted_points[:k]
3. Inefficient for Large n with Small k
When k
is much smaller than n
(e.g., k=10, n=100,000), sorting all points is wasteful. A more efficient approach uses a max-heap:
Heap-based solution (O(n log k) time):
import heapq
def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
# Use a max-heap of size k (negate distances for max-heap behavior)
heap = []
for x, y in points:
dist = -(x*x + y*y) # Negative for max-heap
if len(heap) < k:
heapq.heappush(heap, (dist, [x, y]))
elif dist > heap[0][0]: # Current point is closer than the farthest in heap
heapq.heapreplace(heap, (dist, [x, y]))
return [point for _, point in heap]
4. Floating Point Precision Issues
Using hypot
or square root can introduce floating-point precision errors. For integer coordinates, sticking to integer arithmetic (squared distances) avoids this entirely:
# Prefer integer arithmetic when possible points.sort(key=lambda p: p[0]*p[0] + p[1]*p[1]) # Always exact for integers
Which data structure is used in a depth first search?
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
https assets algo monster cover_photos dnd svg Divide and Conquer The main idea of divide and conquer is to split the main problem into two smaller components assuming that each one of the components had already been solved recursively and then try to solve the bigger problem using the solutions
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
Want a Structured Path to Master System Design Too? Donβt Miss This!