973. K Closest Points to Origin
Problem Description
The problem provides us with an array of points called points
, where each point is itself an array containing an x (x_i
) and y (y_i
) coordinate. These points are located on a two-dimensional plane. Additionally, we're given an integer k
. Our task is to find the k
points from the array that are closest to the origin point (0, 0) based on the Euclidean distance. The Euclidean distance between two points (x1, y1)
and (x2, y2)
on the X-Y plane is the square root of (x1-x2)^2 + (y1-y2)^2
. The solution should return these k
closest points in any order, and it is noted that the points returned will be unique in terms of their position in the output list.
Intuition
To solve this problem, we need to determine how far each point in the array is from the origin. In geometrical terms, we are interested in the magnitude of the vector from the origin to the point, which corresponds to the Euclidean distance. However, since we only care about the relative distances for sorting (finding the smallest distances) and not the exact distances themselves, we can avoid calculating the square root to simplify the comparison. By comparing the squares of the distances, we can maintain the same ordering as we would if we used the actual distances.
Here is the thought process behind the solution:
- We recognize that we need to sort the points by their distance to the origin.
- To avoid unnecessary computation, we can compare the squared distances instead of the actual distances.
- We define a custom sorting key that calculates
x^2 + y^2
for each point, wherex
andy
are the coordinates of that point. - Using the
sort()
function of a list in Python, we sort all points based on their squared distances from the origin. - Finally, we return the first
k
elements of the sorted array, as these will be thek
closest points to the origin.
By applying this approach, we achieve a manageable solution that is easy to implement and understand, using built-in Python functionalities for sorting with a custom key.
Learn more about Math, Divide and Conquer, Sorting and Heap (Priority Queue) patterns.
Solution Approach
The implementation of the solution makes use of Python's built-in sort function and the concept of lambda functions for the custom sorting key.
-
The lambda function is employed as the sorting key for the
sort()
method. This function takes a pointp
as input and returnsp[0] * p[0] + p[1] * p[1]
. This expression equates to the square of the distance from the point to the origin, omitting the square root for the reasons described earlier. -
The algorithm:
- Accepts the list of points and the integer
k
. - Applies the
sort()
function on the list of points. Instead of sorting by a single element, it sorts by the value returned by the lambda function for each element, which represents the squared distance of each point to the origin. - The sorting process rearranges the items of the list in place, meaning that after the
sort()
method is called, the original list is modified to be in sorted order.
- Accepts the list of points and the integer
-
After sorting the full list, a slice of the first
k
elements (points[:k]
) is returned. In Python, slicing a list returns a new list containing the specified range of elements, so in this case, it gives us the closestk
points. -
The use of the
sort()
function and a slicing operation makes the solution concise and effective. The Pythonsort()
method is an efficient, typically O(n log n) algorithm for sorting lists. -
The space complexity is kept to O(1) since the sorting is done in place and only the resulting list slice of size
k
is returned.
In conclusion, the solution leverages the efficiency of Python's sorting algorithm and a concise expression of the distance calculation to produce a clean and efficient algorithm for finding the k
closest points to the origin.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's illustrate the solution approach with a simple example. Imagine we have the following points and we are tasked with finding the k = 2
closest points to the origin (0, 0)
:
points = [[1, 3], [2, -2], [5, 4], [-3, 3]]
Following the thought process behind the solution:
-
We calculate the squared distance from the origin for each point which gives us:
- For point
[1, 3]
:1^2 + 3^2 = 1 + 9 = 10
- For point
[2, -2]
:2^2 + (-2)^2 = 4 + 4 = 8
- For point
[5, 4]
:5^2 + 4^2 = 25 + 16 = 41
- For point
[-3, 3]
:(-3)^2 + 3^2 = 9 + 9 = 18
- For point
-
We sort the points based on their squared distances, without computing the square root:
Sorted points based on squared distances: [[2, -2], [1, 3], [-3, 3], [5, 4]] Sorted squared distances: [8, 10, 18, 41]
-
We employ a lambda function in Python to serve as a custom sorting key:
lambda p: p[0]*p[0] + p[1]*p[1]
. -
We apply this lambda function within the
sort()
method to ourpoints
list to rearrange the list based on the calculated squared distances:points.sort(key=lambda p: p[0]*p[0] + p[1]*p[1])
-
After sorting,
points
looks like this:[[2, -2], [1, 3], [-3, 3], [5, 4]]
. -
We return the first
k
elements of the sorted array to get thek
closest points. Sincek = 2
, we return the first two points:closest_points = points[:k] # closest_points = [[2, -2], [1, 3]]
With this example, we have effectively walked through the solution approach described in the problem content. We sorted the points by their squared distance from the origin, avoided unnecessary square root computation, and returned the k
closest points by slicing the sorted array without altering the original array's order.
Solution Implementation
1from typing import List
2
3class Solution:
4 def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
5 # Sort the given list of points by their Euclidean distance
6 # from the origin (0, 0) without actually computing the square root.
7 # The lambda function computes the squared distance.
8 points.sort(key=lambda point: point[0]**2 + point[1]**2)
9
10 # Return the first k points from the sorted list.
11 return points[:k]
12
1import java.util.Arrays;
2
3class Solution {
4 // This method finds the k closest points to the origin
5 public int[][] kClosest(int[][] points, int k) {
6 // Sort the array of points based on their Euclidean distance from the origin
7 Arrays.sort(points, (point1, point2) -> {
8 // Calculate the squared distance for the first point from the origin
9 int distance1 = point1[0] * point1[0] + point1[1] * point1[1];
10 // Calculate the squared distance for the second point from the origin
11 int distance2 = point2[0] * point2[0] + point2[1] * point2[1];
12 // Compare the two distances
13 return distance1 - distance2;
14 });
15
16 // Return the first k elements of the sorted array, which are the k closest to the origin
17 return Arrays.copyOfRange(points, 0, k);
18 }
19}
20
1#include <vector>
2#include <algorithm>
3
4class Solution {
5public:
6 // Method to find the 'k' closest points to the origin (0, 0) in a 2D plane
7 std::vector<std::vector<int>> kClosest(std::vector<std::vector<int>>& points, int k) {
8 // Sort the 'points' array using a custom comparator
9 std::sort(points.begin(), points.end(), [](const std::vector<int>& pointA, const std::vector<int>& pointB) {
10 // Calculate the squared Euclidean distance from the origin for pointA
11 int distA = pointA[0] * pointA[0] + pointA[1] * pointA[1];
12 // Calculate the squared Euclidean distance from the origin for pointB
13 int distB = pointB[0] * pointB[0] + pointB[1] * pointB[1];
14 // Compare the squared distances to sort the points by distance
15 return distA < distB;
16 });
17
18 // Construct a vector containing the first 'k' elements from the sorted 'points' array
19 return std::vector<std::vector<int>>(points.begin(), points.begin() + k);
20 }
21};
22
1function kClosest(points: number[][], k: number): number[][] {
2 // A comparison function for the .sort() method, which will sort the points
3 // based on their distance squared from the origin in ascending order.
4 const compareDistance = (pointA: number[], pointB: number[]) => {
5 // Calculate the distance squared of pointA from the origin.
6 const distanceA = pointA[0] ** 2 + pointA[1] ** 2;
7 // Calculate the distance squared of pointB from the origin.
8 const distanceB = pointB[0] ** 2 + pointB[1] ** 2;
9 // The return value determines the order of sorting.
10 return distanceA - distanceB;
11 };
12
13 // Sort the array of points with the custom compare function.
14 const sortedPoints = points.sort(compareDistance);
15
16 // Return the first 'k' elements from the sorted array of points.
17 return sortedPoints.slice(0, k);
18}
19
Time and Space Complexity
Time Complexity: The time complexity of the code is O(n log n)
, where n
is the number of points. This arises from the use of the .sort()
method, which has O(n log n)
complexity for sorting the list of points based on their distance from the origin calculated by the key function (lambda p: p[0] * p[0] + p[1] * p[1]
).
Space Complexity: The space complexity of the code is O(n)
. While the sorting is done in-place and does not require additional space proportional to the input, the sorted list of points is stored in the same space that was taken by the input list. Hence, the space complexity is linear with respect to the size of the input list of points.
Learn more about how to find time and space complexity quickly using problem constraints.
A person thinks of a number between 1 and 1000. You may ask any number questions to them, provided that the question can be answered with either "yes" or "no".
What is the minimum number of questions you needed to ask so that you are guaranteed to know the number that the person is thinking?
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 algomonster s3 us east 2 amazonaws com 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
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!