LeetCode 每日一题笔记
0. 前言
- 日期:2025.12.02
- 题目:3623. 统计梯形的数目 I
- 难度:中等
- 标签:数组 哈希表 组合数学
1. 题目理解
问题描述 :
给定二维整数数组 points(每个元素为平面上的点 [x_i, y_i]),统计可以从中选择四个不同点组成的水平梯形数量。水平梯形是至少有一对平行于 x 轴的边的凸四边形。结果需对 (10^9 + 7) 取余。
示例:
输入:
points = [[1,0],[2,0],[3,0],[2,2],[3,2]]输出:
3解释:y=0的点选2个(3种组合),y=2的点选2个(1种组合),乘积为 (3 \times 1 = 3)。
2. 解题思路
核心观察
水平梯形的核心是选择两条不同的水平线(y坐标不同),并从每条线上各选2个点,这4个点即可构成梯形。因此问题转化为:
- 按 y 坐标分组,统计每个 y 对应的点数量;
- 计算每个 y 对应的"选2个点"的组合数 (C(n,2) = \frac{n(n-1)}{2});
- 计算所有不同 y 组合数的两两乘积和(即最终梯形数量)。
算法步骤
- 用哈希表统计每个 y 对应的点数量;
- 遍历哈希表,计算每个 y 的组合数,并维护"组合数总和"和"组合数平方和";
- 利用公式 (\frac{(\sum c_i)^2 - \sum c_i^2}{2}) 计算两两乘积和(避免双重循环);
- 对结果取模并处理负数、除法(用模逆元)。
3. 代码实现
java
class Solution {
private static final int MOD = 1000000007;
public static int countTrapezoids(int[][] points) {
long res = 0;
Map<Integer, Integer> map = new HashMap<>();
// 1. 统计每个y对应的点数量
for (int[] p : points) {
int y = p[1];
map.put(y, map.getOrDefault(y, 0) + 1);
}
long sumC = 0; // 组合数的总和
long sumC2 = 0; // 组合数的平方和
// 2. 计算每个y的组合数,维护总和与平方和
for (int cnt : map.values()) {
long c = (long) cnt * (cnt - 1) / 2; // C(cnt,2)
sumC = (sumC + c) % MOD;
sumC2 = (sumC2 + c * c) % MOD;
}
// 3. 公式计算两两乘积和:(sumC² - sumC2)/2
res = (sumC * sumC - sumC2) % MOD;
res = (res + MOD) % MOD; // 处理负数
res = res * 500000004 % MOD; // 乘以2的模逆元(替代除法)
return (int) res;
}
}
4. 代码优化说明
官方题解采用累加累乘的方式,避免公式推导和模逆元,逻辑更简洁:
java
class Solution {
public int countTrapezoids(int[][] points) {
Map<Integer, Integer> pointNum = new HashMap<>();
final int mod = 1000000007;
long ans = 0, sum = 0;
// 统计每个y的点数量
for (int[] point : points) {
pointNum.put(point[1], pointNum.getOrDefault(point[1], 0) + 1);
}
// 遍历每个y的组合数,累加两两乘积和
for (int pNum : pointNum.values()) {
long edge = (long) pNum * (pNum - 1) / 2;
ans = (ans + edge * sum) % mod; // 当前组合数 × 已遍历组合数的总和
sum = (sum + edge) % mod; // 更新已遍历组合数的总和
}
return (int) ans;
}
}
优化点:
- 无需公式推导,通过动态维护
sum(已遍历组合数的总和),直接累加当前组合数与之前所有组合数的乘积; - 避免模逆元,仅用加法和乘法的模运算,逻辑更直观。
5. 复杂度分析
- 时间复杂度:(O(n + k)),其中 (n) 是点的数量,(k) 是不同 y 的数量(遍历点和哈希表各一次);
- 空间复杂度:(O(k)),哈希表存储不同 y 的点数量。
6. 总结
本题的核心是组合数学 + 哈希表分组,通过将问题转化为"不同 y 组合数的两两乘积和",利用数学公式或累加技巧避免双重循环,实现高效计算。官方题解的累加方式更简洁易写,而公式法更偏数学推导,两者时间复杂度一致。