Facebook Pixel

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:

  1. 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
  2. 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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 extracts p[0] (x-coordinate) and p[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) where n 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 uses O(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 Evaluator

Example 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:

  1. Applies the lambda function to each point to get its distance
  2. 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
Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

Which data structure is used in a depth first search?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More