447 Number of Boomerangs

博客围绕平面上点集的 boomerang 数量计算展开。介绍了 boomerang 是满足特定距离条件的三元组,涉及二维数组表示点坐标。给出两种解法,分析了时间和空间复杂度,还提及使用 HashMap 存储距离信息,以及排列组合在计算中的应用,第二种解法优化了空间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值