来源第 520 场周赛 Q1难度简单分数1161
题目描述
给你一个包含 n 个元素的二维整数数组 intervals,其中 intervals[i] = [starti, endi] 表示从 starti 到 endi 的 闭区间 。
返回满足 0 <= i < j < n,且 intervals[i] 与 intervals[j] 相交 的下标对 (i, j) 的数量。
如果两个区间至少有一个公共点,则称它们 相交。仅共享一个端点的情况也视为相交。
示例 1:
输入: intervals = [[1,2],[2,3],[3,4]]
输出: 2
解释:
共有 2 对相交区间:
- 区间
[1, 2] 和 [2, 3] 在点 2 处相交。 - 区间
[2, 3] 和 [3, 4] 在点 3 处相交。
示例 2:
输入: intervals = [[1,5],[2,4],[3,6]]
输出: 3
解释:
共有 3 对相交区间:
[1, 5] 和 [2, 4] 的交集为 [2, 4]。 [1, 5] 和 [3, 6] 的交集为 [3, 5]。 [2, 4] 和 [3, 6] 的交集为 [3, 4]。
示例 3:
输入: intervals = [[1,2],[3,4],[5,6]]
输出: 0
解释:
不存在相交的区间对。因此,答案为 0。
提示:
2 <= intervals.length <= 100 intervals[i] == [starti, endi] 0 <= starti <= endi <= 100
解法
方法一:排序 + 双指针
思考
\(n \le 100\),枚举全部下标对判断是否相交就能通过。两个闭区间不相交,当且仅当其中一个的右端点严格小于另一个的左端点。
直接枚举要对每个对写两端判断。从 \(\frac{n(n-1)}{2}\) 里减去不相交对更干净:对每个左端点,统计有多少个区间已经在它开始之前结束。
左右端点分别排序后,指针只向右移动,一次扫描就能得到每个起点对应的不相交个数。
两个闭区间 \([l_1, r_1]\)、 \([l_2, r_2]\) 不相交,当且仅当 \(r_1 < l_2\) 或 \(r_2 < l_1\)。
区间对的总数为 \(\frac{n(n-1)}{2}\)。我们统计不相交的对数,再从总数中减去。
将所有左端点、右端点分别升序排列。从左到右枚举每个左端点 \(s\),用指针 \(i\) 维护满足 \(\textit{ends}[i] < s\) 的区间个数,这些区间与当前区间不相交,从答案中减去。
每个不相交对恰好被统计一次:右端点更小的那个区间,会在扫描另一个区间的左端点时被计入。
时间复杂度 \(O(n \times \log n)\),空间复杂度 \(O(n)\)。其中 \(n\) 是区间的数量。
1
2
3
4
5
6
7
8
9
10
11
12 | class Solution:
def countIntersectingIntervals(self, intervals: list[list[int]]) -> int:
n = len(intervals)
starts = sorted(s for s, _ in intervals)
ends = sorted(e for _, e in intervals)
ans = n * (n - 1) // 2
i = 0
for start in starts:
while i < n and ends[i] < start:
i += 1
ans -= i
return ans
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | class Solution {
public int countIntersectingIntervals(int[][] intervals) {
int n = intervals.length;
int[] starts = new int[n];
int[] ends = new int[n];
for (int i = 0; i < n; i++) {
starts[i] = intervals[i][0];
ends[i] = intervals[i][1];
}
Arrays.sort(starts);
Arrays.sort(ends);
int ans = n * (n - 1) / 2;
int i = 0;
for (int start : starts) {
while (i < n && ends[i] < start) {
i++;
}
ans -= i;
}
return ans;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | class Solution {
public:
int countIntersectingIntervals(vector<vector<int>>& intervals) {
int n = intervals.size();
vector<int> starts(n), ends(n);
for (int i = 0; i < n; i++) {
starts[i] = intervals[i][0];
ends[i] = intervals[i][1];
}
sort(starts.begin(), starts.end());
sort(ends.begin(), ends.end());
int ans = n * (n - 1) / 2;
int i = 0;
for (int start : starts) {
while (i < n && ends[i] < start) {
i++;
}
ans -= i;
}
return ans;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | func countIntersectingIntervals(intervals [][]int) int {
n := len(intervals)
starts := make([]int, n)
ends := make([]int, n)
for i, p := range intervals {
starts[i] = p[0]
ends[i] = p[1]
}
slices.Sort(starts)
slices.Sort(ends)
ans := n * (n - 1) / 2
i := 0
for _, start := range starts {
for i < n && ends[i] < start {
i++
}
ans -= i
}
return ans
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | function countIntersectingIntervals(intervals: number[][]): number {
const n = intervals.length;
const starts = intervals.map(([s]) => s).sort((a, b) => a - b);
const ends = intervals.map(([, e]) => e).sort((a, b) => a - b);
let ans = (n * (n - 1)) / 2;
let i = 0;
for (const start of starts) {
while (i < n && ends[i] < start) {
i++;
}
ans -= i;
}
return ans;
}
|