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]]
这一题属于easy难度,不太困难。
首选的想法可能是3层循环暴力解法,这样会到O(n^3),显然是不可取的。
考虑到它只关心有多少组这样的数,而不关心有到底是哪几个数,所以有个O(n^2)解法。
思路:
针对一个点,我们算出来它到所有其他的距离。也就是说,只要我们找到某两个点到该点的距离相等,那么就可以找到一个三元组,由于题目里面说交换这两个点的位置算两组,这样算两个“boomerang”。如果找到了三个点到该点的距离相当,就可以形成6个boomerang。以此类推。
于是,我们想如何找这些距离相等的点的数量呢,很简单,用一个map存储,key=距离,value=数量。遍历这个map的values,计算所有的values就可以了。
该方法时间复杂度O(n^2),空间复杂度O(n):
public class Solution {
public int numberOfBoomerangs(int[][] points) {
int nResult = 0;
for (int i = 0; i < points.length; i++) {
Map<Integer, Integer> map = new HashMap<>();
for (int j = 0; j < points.length; j++) {
if (i == j) {
continue;
}
int nDis = getInstance(points[i], points[j]);
map.put(nDis, map.getOrDefault(nDis, 0) + 1);
}
for (int ii : map.values()) {
nResult += ii * (ii - 1);
}
}
return nResult;
}
private int getInstance(int[] p1, int[] p2) {
return (p1[0] - p2[0]) * (p1[0] - p2[0]) + (p1[1] - p2[1]) * (p1[1] - p2[1]);
}
}
上面代码用时271ms,注意一个细节,在内存循环开始之前,每次我都是new了一个map,我修改一下,成为如下代码,用时163ms:
public class Solution {
public int numberOfBoomerangs(int[][] points) {
int nResult = 0;
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < points.length; i++) {
for (int j = 0; j < points.length; j++) {
if (i == j) {
continue;
}
int nDis = getInstance(points[i], points[j]);
map.put(nDis, map.getOrDefault(nDis, 0) + 1);
}
for (int ii : map.values()) {
nResult += ii * (ii - 1);
}
map.clear();
}
return nResult;
}
private int getInstance(int[] p1, int[] p2) {
return (p1[0] - p2[0]) * (p1[0] - p2[0]) + (p1[1] - p2[1]) * (p1[1] - p2[1]);
}
}
注意到map的位置,放到了循环外,并且在使用完以后,用了map.clear(),来清空这个map中的数据。map得到了重用,而不是每次重新new一个map,效率得到了巨大提升。也就是说,new一个map消耗的资源是非常高的。从源码中我们可以看到,hashmap.clear(),实际就是将table中所有的内容置为null。另外,很重要的是当再次put数据的时候,也起到了重用的效果。
/**
* Removes all of the mappings from this map.
* The map will be empty after this call returns.
*/
public void clear() {
Node<K,V>[] tab;
modCount++;
if ((tab = table) != null && size > 0) {
size = 0;
for (int i = 0; i < tab.length; ++i)
tab[i] = null;
}
}
而table,就是node的集合,node就是keyValue对。
/**
* The table, initialized on first use, and resized as
* necessary. When allocated, length is always a power of two.
* (We also tolerate length zero in some operations to allow
* bootstrapping mechanics that are currently not needed.)
*/
transient Node<K,V>[] table;
我们还可以进一步进行改进,用一个辅助数组来存储距离,也就是说算过一次的距离,不需要再算一次了,用时183ms。事实上这种改进对性能的提升非常小,这是因为一个距离只需要算两次,也就是说算一次再存起来的消耗,还不如算两次的。不像dp里面,算的距离需要用很多次。
public class Solution {
public int numberOfBoomerangs(int[][] points) {
int nResult = 0;
int[][] nDisMatrix = new int[points.length][points.length];
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < points.length; i++) {
for (int j = 0; j < points.length; j++) {
if (i == j) {
continue;
}
int nDis = 0;
if (nDisMatrix[i][j] > 0) {
nDis = nDisMatrix[i][j];
} else {
nDis = getInstance(points[i], points[j]);
nDisMatrix[i][j] = nDis;
nDisMatrix[j][i] = nDis;
}
map.put(nDis, map.getOrDefault(nDis, 0) + 1);
}
for (int ii : map.values()) {
nResult += ii * (ii - 1);
}
map.clear();
}
return nResult;
}
private int getInstance(int[] p1, int[] p2) {
return (p1[0] - p2[0]) * (p1[0] - p2[0]) + (p1[1] - p2[1]) * (p1[1] - p2[1]);
}
}