K Closest Points to Origin
Given a list of points, return the k closest points to the origin (0, 0).
Note: The distance between two points (x₁, y₁) and (x₂, y₂) is calculated as √[(x₂-x₁)² + (y₂-y₁)²]. For distance to origin, this simplifies to √(x² + y²).
Example(s)
Example 1:
Input: points = [[1,1],[-2,-2]], k = 1
Output: [[1,1]]
Explanation:
Distance from origin:
- [1,1]: √(1² + 1²) = √2 ≈ 1.41
- [-2,-2]: √((-2)² + (-2)²) = √8 ≈ 2.83
The closest point is [1,1].
Example 2:
Input: points = [[3,3],[5,-1],[-2,4]], k = 2
Output: [[3,3],[-2,4]]
Explanation:
Distance from origin:
- [3,3]: √(3² + 3²) = √18 ≈ 4.24
- [5,-1]: √(5² + (-1)²) = √26 ≈ 5.10
- [-2,4]: √((-2)² + 4²) = √20 ≈ 4.47
The 2 closest are [3,3] and [-2,4].
Example 3:
Input: points = [[1,3],[-2,2]], k = 1
Output: [[-2,2]]
Explanation:
Distance from origin:
- [1,3]: √(1² + 3²) = √10 ≈ 3.16
- [-2,2]: √((-2)² + 2²) = √8 ≈ 2.83
The closest point is [-2,2].
Solution
The solution uses sorting by distance:
- Calculate distances: For each point, calculate distance squared (avoid sqrt for efficiency)
- Sort by distance: Sort points by their distance to origin
- Return top k: Return the first k points from the sorted list
- JavaScript Solution
- Python Solution
JavaScript Solution - Sorting
/**
* Find k closest points to origin
* @param {number[][]} points - Array of [x, y] coordinates
* @param {number} k - Number of closest points to return
* @return {number[][]} - K closest points
*/
function kClosest(points, k) {
// Calculate distance squared for each point (avoid sqrt for efficiency)
// Distance² = x² + y²
const pointsWithDistance = points.map((point, index) => ({
point,
distanceSquared: point[0] * point[0] + point[1] * point[1],
index
}));
// Sort by distance squared
pointsWithDistance.sort((a, b) => a.distanceSquared - b.distanceSquared);
// Return first k points
return pointsWithDistance.slice(0, k).map(item => item.point);
}
// Test cases
console.log('Example 1:', kClosest([[1,1],[-2,-2]], 1));
// [[1,1]]
console.log('Example 2:', kClosest([[3,3],[5,-1],[-2,4]], 2));
// [[3,3],[-2,4]]
console.log('Example 3:', kClosest([[1,3],[-2,2]], 1));
// [[-2,2]]
console.log('Test 4:', kClosest([[1,3],[-2,2],[2,-2]], 2));
// [[-2,2],[1,3]]Output:
Click "Run Code" to execute the code and see the results.
Python Solution - Sorting
from typing import List
def k_closest(points: List[List[int]], k: int) -> List[List[int]]:
"""
Find k closest points to origin
Args:
points: List of [x, y] coordinates
k: Number of closest points to return
Returns:
List[List[int]]: K closest points
"""
# Sort by distance squared (avoid sqrt for efficiency)
# Distance² = x² + y²
points.sort(key=lambda p: p[0] * p[0] + p[1] * p[1])
# Return first k points
return points[:k]
# Test cases
print('Example 1:', k_closest([[1,1],[-2,-2]], 1))
# [[1,1]]
print('Example 2:', k_closest([[3,3],[5,-1],[-2,4]], 2))
# [[3,3],[-2,4]]
print('Example 3:', k_closest([[1,3],[-2,2]], 1))
# [[-2,2]]
print('Test 4:', k_closest([[1,3],[-2,2],[2,-2]], 2))
# [[-2,2],[1,3]]Loading Python runtime...
Output:
Click "Run Code" to execute the code and see the results.
Alternative Solution (Max Heap)
For better performance when k is much smaller than n, use a max heap:
- JavaScript Max Heap
- Python Max Heap
/**
* Max heap approach (better when k << n)
*/
function kClosestHeap(points, k) {
// Max heap of size k (keep k smallest)
const heap = [];
for (const point of points) {
const distanceSquared = point[0] * point[0] + point[1] * point[1];
if (heap.length < k) {
heap.push({ point, distance: distanceSquared });
heapifyUp(heap, heap.length - 1);
} else {
// If current point is closer than farthest in heap
if (distanceSquared < heap[0].distance) {
heap[0] = { point, distance: distanceSquared };
heapifyDown(heap, 0);
}
}
}
return heap.map(item => item.point);
}
// Helper functions for max heap
function heapifyUp(heap, index) {
while (index > 0) {
const parent = Math.floor((index - 1) / 2);
if (heap[parent].distance >= heap[index].distance) break;
[heap[parent], heap[index]] = [heap[index], heap[parent]];
index = parent;
}
}
function heapifyDown(heap, index) {
while (true) {
let largest = index;
const left = 2 * index + 1;
const right = 2 * index + 2;
if (left < heap.length && heap[left].distance > heap[largest].distance) {
largest = left;
}
if (right < heap.length && heap[right].distance > heap[largest].distance) {
largest = right;
}
if (largest === index) break;
[heap[index], heap[largest]] = [heap[largest], heap[index]];
index = largest;
}
}
import heapq
def k_closest_heap(points: List[List[int]], k: int) -> List[List[int]]:
"""
Max heap approach (better when k << n)
"""
# Max heap of size k (Python's heapq is min-heap, so negate distances)
heap = []
for point in points:
distance_squared = point[0] * point[0] + point[1] * point[1]
if len(heap) < k:
heapq.heappush(heap, (-distance_squared, point))
else:
# If current point is closer than farthest in heap
if distance_squared < -heap[0][0]:
heapq.heapreplace(heap, (-distance_squared, point))
return [point for _, point in heap]
Alternative: Quickselect
For optimal average-case performance, use quickselect:
- JavaScript Quickselect
- Python Quickselect
/**
* Quickselect approach (O(n) average time)
*/
function kClosestQuickselect(points, k) {
function distanceSquared(point) {
return point[0] * point[0] + point[1] * point[1];
}
function quickselect(left, right, k) {
if (left === right) return;
const pivotIndex = partition(left, right);
if (pivotIndex === k) {
return;
} else if (pivotIndex < k) {
quickselect(pivotIndex + 1, right, k);
} else {
quickselect(left, pivotIndex - 1, k);
}
}
function partition(left, right) {
const pivotDistance = distanceSquared(points[right]);
let i = left;
for (let j = left; j < right; j++) {
if (distanceSquared(points[j]) <= pivotDistance) {
[points[i], points[j]] = [points[j], points[i]];
i++;
}
}
[points[i], points[right]] = [points[right], points[i]];
return i;
}
quickselect(0, points.length - 1, k);
return points.slice(0, k);
}
def k_closest_quickselect(points: List[List[int]], k: int) -> List[List[int]]:
"""
Quickselect approach (O(n) average time)
"""
def distance_squared(point):
return point[0] * point[0] + point[1] * point[1]
def quickselect(left, right, k):
if left == right:
return
pivot_index = partition(left, right)
if pivot_index == k:
return
elif pivot_index < k:
quickselect(pivot_index + 1, right, k)
else:
quickselect(left, pivot_index - 1, k)
def partition(left, right):
pivot_distance = distance_squared(points[right])
i = left
for j in range(left, right):
if distance_squared(points[j]) <= pivot_distance:
points[i], points[j] = points[j], points[i]
i += 1
points[i], points[right] = points[right], points[i]
return i
quickselect(0, len(points) - 1, k)
return points[:k]
Complexity
- Time Complexity:
- Sorting: O(n log n) - Where n is the number of points
- Max Heap: O(n log k) - Better when k
<<n - Quickselect: O(n) average, O(n²) worst case
- Space Complexity:
- Sorting: O(1) or O(n) depending on sorting algorithm
- Max Heap: O(k) - For the heap
- Quickselect: O(1) - In-place partitioning
Approach
The solution uses sorting by distance:
-
Calculate distance squared:
- For each point [x, y], calculate x² + y²
- Use squared distance to avoid expensive sqrt operation
- Squared distance preserves ordering (if a² < b², then a < b)
-
Sort points:
- Sort points by their distance squared to origin
- Closest points will be first
-
Return top k:
- Return the first k points from sorted list
Key Insights
- Distance squared: Use x² + y² instead of √(x² + y²) to avoid sqrt
- Squared preserves order: If a² < b², then a < b (for positive values)
- Sorting is simple: O(n log n) but easy to implement
- Heap optimization: Better when k is much smaller than n
- Quickselect: Optimal average case but more complex
Step-by-Step Example
Let's trace through Example 1: points = [[1,1],[-2,-2]], k = 1
Step 1: Calculate distance squared
Point [1,1]:
distance² = 1² + 1² = 1 + 1 = 2
Point [-2,-2]:
distance² = (-2)² + (-2)² = 4 + 4 = 8
Step 2: Sort by distance squared
[1,1] has distance² = 2
[-2,-2] has distance² = 8
Sorted: [[1,1], [-2,-2]]
Step 3: Return first k = 1
Result: [[1,1]]
Visual Representation
Example 1: points = [[1,1],[-2,-2]], k = 1
Points on coordinate plane:
|
| [1,1] (distance = √2)
|
--------+--------
|
| [-2,-2] (distance = √8)
|
Distance squared:
[1,1]: 2
[-2,-2]: 8
Sorted: [1,1] (closest)
Result: [[1,1]]
Edge Cases
- k equals array length: Return all points
- k = 1: Return the single closest point
- Points at same distance: Any of them can be returned
- Negative coordinates: Works correctly (squared is always positive)
- Origin point: [0,0] has distance 0 (closest)
Important Notes
- Distance squared: Use x² + y² instead of √(x² + y²) for efficiency
- Ordering preserved: Squared distance preserves relative order
- No sqrt needed: Avoids floating-point operations
- Stable sort: If distances are equal, order may vary
- In-place vs new array: Can modify input or return new array
Optimization: Distance Squared
Why use distance squared?
- √(x² + y²) is expensive (square root operation)
- x² + y² preserves ordering (if a² < b², then a < b for a, b > 0)
- No need for actual distance, only relative ordering matters
Comparison of Approaches
| Approach | Time | Space | Best For |
|---|---|---|---|
| Sorting | O(n log n) | O(1) | General purpose, simple |
| Max Heap | O(n log k) | O(k) | When k << n |
| Quickselect | O(n) avg | O(1) | When k is close to n/2 |
Related Problems
- Kth Largest Element: Find kth largest element
- Top K Frequent Elements: Different selection problem
- Merge K Sorted Lists: Different problem
- Closest Pair of Points: Different geometric problem
Takeaways
- Distance squared avoids expensive sqrt operation
- Sorting is simple and works well
- Heap optimization is better when k is small
- Quickselect provides optimal average case
- O(n log n) is acceptable for most cases