Number of Boomerangs
MedPrompt
You are given n points in the plane that are all distinct, where points[i] = [xi, yi]. A boomerang is a tuple of points (i, j, k) such that the distance between i and j equals the distance between i and k (the order of the tuple matters).
Return the number of boomerangs.
Example 1:
Input: points = [[0,0],[1,0],[2,0]]
Output: 2
Explanation: The two boomerangs are [[1,0],[0,0],[2,0]] and [[1,0],[2,0],[0,0]].Example 2:
Input: points = [[1,1],[2,2],[3,3]]
Output: 2Example 3:
Input: points = [[1,1]]
Output: 0
Constraints:
n == points.length1 <= n <= 500points[i].length == 2-104 <= xi, yi <= 104- All the points are unique.
Approaches
2 approaches with complexity analysis and trade-offs.
This approach involves iterating through all possible combinations of three distinct points (i, j, k) and checking if they form a boomerang. A boomerang is defined by the condition that the distance from point i to j is equal to the distance from point i to k.
Algorithm
- Initialize a counter
countto 0. - Use three nested loops to iterate through all unique triplets of indices
(i, j, k). - For each triplet, calculate the squared distance between
points[i]andpoints[j], and the squared distance betweenpoints[i]andpoints[k]. - If the two distances are equal, increment the
count. - After checking all triplets, return
count.
Walkthrough
This approach directly translates the problem definition into code. We check every possible ordered triplet of distinct points (i, j, k) to see if it satisfies the boomerang condition: distance(i, j) == distance(i, k). To avoid floating-point precision issues and the performance cost of square roots, we compare the squared distances instead. The squared distance between two points (x1, y1) and (x2, y2) is (x1 - x2)^2 + (y1 - y2)^2. The implementation involves three nested loops to select the three points, followed by a check for the distance equality.
class Solution { public int numberOfBoomerangs(int[][] points) { int n = points.length; if (n < 3) { return 0; } int count = 0; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (i == j) continue; for (int k = 0; k < n; k++) { if (i == k || j == k) continue; long dist_ij = getSquaredDistance(points[i], points[j]); long dist_ik = getSquaredDistance(points[i], points[k]); if (dist_ij == dist_ik) { count++; } } } } return count; } private long getSquaredDistance(int[] p1, int[] p2) { long dx = p1[0] - p2[0]; long dy = p1[1] - p2[1]; return dx * dx + dy * dy; }}Complexity
Time
O(n^3), where n is the number of points. We have three nested loops, each iterating up to n times. This makes the approach very slow for larger inputs.
Space
O(1). We only use a few variables to store indices and the count, so the extra space required is constant.
Trade-offs
Pros
Simple to understand and implement.
Cons
Highly inefficient due to the cubic time complexity.
Likely to result in a 'Time Limit Exceeded' error for the given constraints (n <= 500).
A more efficient approach is to iterate through each point and consider it as the pivot of a potential boomerang. For each pivot point, we can use a hash map to count how many other points are at the same distance from it. This avoids the third loop of the brute-force method.
Algorithm
- Initialize
totalBoomerangs = 0. - Iterate through each point
p_ito serve as the pivot. - For each pivot
p_i, create aHashMapto store frequencies of distances to other points. - Iterate through all other points
p_jand populate the map with(squared_distance, count). - After populating the map, iterate through its values (
m). - For each count
m, addm * (m - 1)tototalBoomerangs. - After iterating through all pivots, return
totalBoomerangs.
Walkthrough
We can significantly optimize the process by changing our perspective. Instead of checking every triplet, we can fix one point i as the pivot and then find how many pairs (j, k) exist that are equidistant from i. A hash map is the perfect tool for this.
For each point i, we iterate through all other points j, calculate the squared distance d between i and j, and store these distances in a hash map. The map will have the distance d as the key and the number of points at that distance as the value.
Once the map is built for pivot i, we can iterate through it. If m points are found at a certain distance, we know we can form m * (m - 1) boomerangs. This is a permutation calculation (P(m, 2)), as we need to choose an ordered pair of two distinct points from the m available points to be j and k. We sum these counts for all distances and all pivots to get the final answer.
import java.util.HashMap;import java.util.Map; class Solution { public int numberOfBoomerangs(int[][] points) { int totalBoomerangs = 0; int n = points.length; for (int i = 0; i < n; i++) { Map<Long, Integer> distanceMap = new HashMap<>(); for (int j = 0; j < n; j++) { if (i == j) { continue; } long dist = getSquaredDistance(points[i], points[j]); distanceMap.put(dist, distanceMap.getOrDefault(dist, 0) + 1); } for (int count : distanceMap.values()) { totalBoomerangs += count * (count - 1); } } return totalBoomerangs; } private long getSquaredDistance(int[] p1, int[] p2) { long dx = p1[0] - p2[0]; long dy = p1[1] - p2[1]; return dx * dx + dy * dy; }}Complexity
Time
O(n^2), where n is the number of points. The outer loop runs n times (for each pivot). The inner loop also runs n times to calculate distances and populate the hash map. This is a significant improvement over the brute-force approach.
Space
O(n). For each pivot point, we use a hash map to store distances. In the worst-case scenario, all other n-1 points are at distinct distances from the pivot, so the map will store n-1 entries.
Trade-offs
Pros
Much more efficient with a quadratic time complexity.
Passes the time limits for the given constraints.
Cons
Uses extra space for the hash map.
Solutions
Solution
class Solution { public int numberOfBoomerangs ( int [][] points ) { int ans = 0 ; for ( int [] p1 : points ) { Map < Integer , Integer > cnt = new HashMap <>(); for ( int [] p2 : points ) { int d = ( p1 [ 0 ] - p2 [ 0 ]) * ( p1 [ 0 ] - p2 [ 0 ]) + ( p1 [ 1 ] - p2 [ 1 ]) * ( p1 [ 1 ] - p2 [ 1 ]); cnt . merge ( d , 1 , Integer: : sum ); } for ( int x : cnt . values ()) { ans += x * ( x - 1 ); } } return ans ; } }class Solution { public: int numberOfBoomerangs ( vector < vector < int >>& points ) { int ans = 0 ; for ( auto & p1 : points ) { unordered_map < int , int > cnt ; for ( auto & p2 : points ) { int d = ( p1 [ 0 ] - p2 [ 0 ]) * ( p1 [ 0 ] - p2 [ 0 ]) + ( p1 [ 1 ] - p2 [ 1 ]) * ( p1 [ 1 ] - p2 [ 1 ]); cnt [ d ] ++ ; } for ( auto & [ _ , x ] : cnt ) { ans += x * ( x - 1 ); } } return ans ; } };class Solution : def numberOfBoomerangs ( self , points : List [ List [ int ]]) -> int : ans = 0 for p1 in points : cnt = Counter () for p2 in points : d = dist ( p1 , p2 ) cnt [ d ] += 1 ans += sum ( x * ( x - 1 ) for x in cnt . values ()) return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.