Medium
题目描述
给你一个二维整数数组 points,其中 points[i] = [xi, yi] 表示笛卡尔平面上第 i 个点的坐标。
水平梯形是一个凸四边形,至少有一对水平边(即平行于 x 轴)。当且仅当两条直线具有相同的斜率时,它们才平行。
返回通过从 points 中选择任意四个不同的点可以形成的唯一水平梯形的数量。
由于答案可能很大,请返回对 10^9 + 7 取模的结果。
示例 1:
输入:points = [[1,0],[2,0],[3,0],[2,2],[3,2]]
输出:3
解释:
有三种不同的方式选择四个点形成水平梯形:
- 使用点 [1,0], [2,0], [3,2], [2,2]
- 使用点 [2,0], [3,0], [3,2], [2,2]
- 使用点 [1,0], [3,0], [3,2], [2,2]
示例 2:
输入:points = [[0,0],[1,0],[0,1],[2,1]]
输出:1
解释:
只有一个水平梯形可以被形成。
约束条件:
4 <= points.length <= 10^5-10^8 <= xi, yi <= 10^8- 所有点都是两两不同的
提示:
- 对于平行于 x 轴的直线,其所有点必须具有相同的 y 坐标
- 按 y 坐标对点进行分组
- 选择两个不同的组(两条水平线),从每组中选择两个点形成梯形
解题思路
解题思路
要形成水平梯形,我们需要至少一对平行于 x 轴的边。这意味着我们需要找到两条水平线(具有相同 y 坐标的点),然后从每条线上选择两个点。
核心思路:
- 按 y 坐标分组:将所有点按其 y 坐标进行分组,每个组代表一条水平线
- 选择两条水平线:从所有水平线中选择两条不同的线
- 计算组合数:对于每对水平线,如果第一条线有 m 个点,第二条线有 n 个点,那么可以形成的梯形数量为 C(m,2) × C(n,2),其中 C(n,k) 表示从 n 个元素中选择 k 个的组合数
算法步骤:
- 遍历所有点,按 y 坐标分组存储
- 对于每个 y 坐标,记录该水平线上的点数
- 枚举所有可能的水平线对,计算每对能形成的梯形数量
- 累加所有结果并取模
这种方法的时间复杂度为 O(n + k²),其中 n 是点的总数,k 是不同 y 坐标的数量。空间复杂度为 O(k)。
代码实现
class Solution {
public:
int countTrapezoids(vector<vector<int>>& points) {
const int MOD = 1e9 + 7;
map<int, int> yCount;
// 按y坐标分组计数
for (auto& point : points) {
yCount[point[1]]++;
}
vector<int> counts;
for (auto& p : yCount) {
if (p.second >= 2) {
counts.push_back(p.second);
}
}
long long result = 0;
int n = counts.size();
// 枚举所有可能的水平线对
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
long long count1 = (long long)counts[i] * (counts[i] - 1) / 2;
long long count2 = (long long)counts[j] * (counts[j] - 1) / 2;
result = (result + count1 * count2) % MOD;
}
}
return result;
}
};
class Solution:
def countTrapezoids(self, points: List[List[int]]) -> int:
MOD = 10**9 + 7
# 按y坐标分组计数
y_count = {}
for x, y in points:
y_count[y] = y_count.get(y, 0) + 1
# 只保留至少有2个点的水平线
counts = [count for count in y_count.values() if count >= 2]
result = 0
n = len(counts)
# 枚举所有可能的水平线对
for i in range(n):
for j in range(i + 1, n):
# 计算组合数 C(counts[i], 2) * C(counts[j], 2)
count1 = counts[i] * (counts[i] - 1) // 2
count2 = counts[j] * (counts[j] - 1) // 2
result = (result + count1 * count2) % MOD
return result
public class Solution {
public int CountTrapezoids(int[][] points) {
const int MOD = 1000000007;
var yCount = new Dictionary<int, int>();
// 按y坐标分组计数
foreach (var point in points) {
int y = point[1];
yCount[y] = yCount.GetValueOrDefault(y, 0) + 1;
}
var counts = new List<int>();
foreach (var count in yCount.Values) {
if (count >= 2) {
counts.Add(count);
}
}
long result = 0;
int n = counts.Count;
// 枚举所有可能的水平线对
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
long count1 = (long)counts[i] * (counts[i] - 1) / 2;
long count2 = (long)counts[j] * (counts[j] - 1) / 2;
result = (result + count1 * count2) % MOD;
}
}
return (int)result;
}
}
var countTrapezoids = function(points) {
const MOD = 1e9 + 7;
const yCount = new Map();
// 按y坐标分组计数
for (const [x, y] of points) {
yCount.set(y, (yCount.get(y) || 0) + 1);
}
const counts = [];
for (const count of yCount.values()) {
if (count >= 2) {
counts.push(count);
}
}
let result = 0;
const n = counts.length;
// 枚举所有可能的水平线对
for (let i = 0; i < n; i++) {
for (let j = i + 1; j < n; j++) {
const count1 = Math.floor(counts[i] * (counts[i] - 1) / 2);
const count2 = Math.floor(counts[j] * (counts[j] - 1) / 2);
result = (result + count1 * count2) % MOD;
}
}
return result;
};
复杂度分析
| 复杂度类型 | 分析 |
|---|---|
| 时间复杂度 | O(n + k²),其中 n 是点的总数,k 是不同 y 坐标的数量 |
| 空间复杂度 | O(k),用于存储每个 y 坐标对应的点数 |