Given n points in the plane that are all pairwise distinct, 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).
Find the number of boomerangs. You may assume that n will be at most 500 and coordinates of points are all in the range [-10000, 10000] (inclusive).
Example:
Input:
[[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]]
解法思路(一)
关于二维数组
- 首先,平面上点的坐标是用二维数组
points
给出的; - 对于二维数组
int[][] points
,第一个[]
控制横坐标,第二个[]
控制纵坐标; - 二维数组的长度是由横坐标决定的;
关于 boomerang
- boomerang 是个三元组;
- 三元组中的每个元表示的是平面上的一个点;
- 平面上的一个点用一个长度为 2 的一维数组表示:{x, y};
boomerang 的几何意义
- 点 i 到点 j 和到点 k 的距离相等,这样的三个点构成了 2 个 boomerang:(i, j, k),(i, k, j);
- 找出所有的 boomerang 就是从每个 a 点开始,计算 a 和其他所有点的距离,到 a 距离相等的所有点可以构成 boomerang;
计算 boomerang 涉及到的数据结构
- HashMap;
- 将到点 a 的距离存入 HashMap 中,key 是距离,value 是到 a 这个距离的点有几个;
计算 boomerang 涉及到的排列组合
- 如果到 a 的距离为 d 的边已经有 1 条,那么第 2 条边只能和这 1 条边组成 2 个 boomerang;
- 如果到 a 的距离为 d 的边已经有 2 条,那么第 3 条边可以和这 2 条边组成 4 个 boomerang;
- 如果到 a 的距离为 d 的边已经有 3 条,那么第 4 条边可以和这 3 条边组成 6 个 boomerang;
- 以此类推 ...
解法实现(一)
时间复杂度
- O(N2);
空间复杂度
- O(N2);
关键字
坐标间距离
哈希表
HashMap
二维数组
用二维数组表示多个坐标
package leetcode._447;
import java.util.HashMap;
public class Solution447_1 {
public int numberOfBoomerangs(int[][] points) {
HashMap<Double, Integer> distance
= new HashMap<>(points.length * points.length);
int boomerangs = 0;
for (int i = 0; i < points.length; i++) {
for (int j = 0; j < points.length; j++) {
if (j != i) {
double d = distanceCalculator(points[i][0],
points[i][1], points[j][0], points[j][1]);
if (distance.containsKey(d)) {
boomerangs = boomerangs + 2 * distance.get(d);
distance.put(d, distance.get(d) + 1);
} else {
distance.put(d, 1);
}
}
}
distance.clear();
}
return boomerangs;
}
private double distanceCalculator(int a, int b, int c, int d) {
double distance = Math.sqrt(
Math.pow(Math.abs(a - c), 2) + Math.pow(Math.abs(b - d), 2)
);
return distance;
}
public static void main(String[] args) {
// int[][] points = {{0,0}, {1,0}, {2,0}};
int[][] points = {{0,0}, {1,0}, {-1,0}, {0,1}, {0,-1}};
int result = (new Solution447_1()).numberOfBoomerangs(points);
System.out.println(result);
}
/**
* 对于二维数组:
* 第一个坐标控制横轴;
* 第二个坐标控制纵轴;
*/
private static void testTwoDimensionArr() {
int[][] arr = {{1, 2},{3, 4, 4},{5, 6}};
System.out.println(arr.length);
System.out.println(arr[1][2]);
}
}
解法实现(二)
时间复杂度
- O(N2);
空间复杂度
- O(N);
实现细节
- 对 HashMap 的声明放在了外层循环和内层循环之间,这使得空间复杂度从 O(N2) 降到了 O(N);
- 对两点之间距离的计算没有开根号,这样保证结果还是整数,虽然不是真正的距离,但同比例放大后,其相对距离是不变的,注意,由于没开根号,可能导致最后的结果太大,导致整型溢出,次问题规模不存在这个问题;
- 对 boomerangs 的统计在内循环完成后,统一遍历 HashMap,注意其计算方式与解法实现(一)不同,比如 d=1 的有 4 个点,每个点会与其他 3 个点形成 3 个 boomerangs,那么 4 个点一共会形成的 boomerangs 个数就是 4 * (4-1);
关键词
最小化开辟辅助空间
避免开根号
十字架图
十字架图 & boomerangs
import java.util.HashMap;
// 447. Number of Boomerangs
// https://leetcode.com/problems/number-of-boomerangs/description/
// 时间复杂度: O(n^2)
// 空间复杂度: O(n)
public class Solution {
public int numberOfBoomerangs(int[][] points) {
int res = 0;
for( int i = 0 ; i < points.length ; i ++ ){
// record中存储 点i 到所有其他点的距离出现的频次
HashMap<Integer, Integer> record = new HashMap<Integer, Integer>();
for(int j = 0 ; j < points.length ; j ++)
if(j != i){
// 计算距离时不进行开根运算, 以保证精度
int dis = dis(points[i], points[j]);
if(record.containsKey(dis))
record.put(dis, record.get(dis) + 1);
else
record.put(dis, 1);
}
for(Integer dis: record.keySet())
res += record.get(dis) * (record.get(dis) - 1);
}
return res;
}
private int dis(int[] pa, int pb[]){
return (pa[0] - pb[0]) * (pa[0] - pb[0]) +
(pa[1] - pb[1]) * (pa[1] - pb[1]);
}
public static void main(String[] args) {
int[][] points = {{0, 0}, {1, 0}, {2, 0}};
System.out.println((new Solution()).numberOfBoomerangs(points));
}
}