Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ tags:
<strong>输入:</strong>intervals = [[1,3],[1,4],[2,5],[3,5]]
<strong>输出:</strong>3
<strong>解释:</strong>nums = [2, 3, 4].
可以证明不存在元素数量为 2 的包含集合。
可以证明不存在元素数量为 2 的包含集合。
</pre>

<p><strong class="example">示例 3:</strong></p>
Expand All @@ -54,7 +54,7 @@ tags:
<strong>输入:</strong>intervals = [[1,2],[2,3],[2,4],[4,5]]
<strong>输出:</strong>5
<strong>解释:</strong>nums = [1, 2, 3, 4, 5].
可以证明不存在元素数量为 4 的包含集合。
可以证明不存在元素数量为 4 的包含集合。
</pre>

<p>&nbsp;</p>
Expand All @@ -75,9 +75,31 @@ tags:

### 方法一:排序 + 贪心

相似题目:
我们希望在数轴上选出尽可能少的整数点,使得每个区间都至少包含两个点。一个经典而有效的策略是按照区间的右端点进行排序,并尽量让已选取的点位于区间的右侧,以便这些点能覆盖更多后续区间。

首先将所有区间按照如下规则排序:

1. 按右端点从小到大;
2. 若右端点相同,按左端点从大到小。

这样排序的原因是:右端点越小的区间“可操作空间”越少,应优先满足;当右端点相同时,左端点更大的区间更窄,更应优先被处理。

随后,我们使用两个变量 $s$ 和 $e$ 分别记录当前所有已处理区间所共同拥有的 **倒数第二个点****最后一个点**。初始时 $s = e = -1$,表示还没有放置任何点。

接下来依次处理排序后的区间 $[a, b]$,根据它与 $\{s, e\}$ 的关系分三种情况讨论:

1. **若 $a \leq s$**
当前区间已包含 $s$ 和 $e$ 两个点,无需额外放点。

2. **若 $s < a \leq e$**
当前区间只包含一个点(即 $e$),还需要补一个点。为了让新点对后续区间最有帮助,我们选择在区间最右侧的点 $b$。此时更新 $\textit{ans} = \textit{ans} + 1$,并将新的两点设为 $\{e, b\}$。

- [452. 用最少数量的箭引爆气球](https://github.com/doocs/leetcode/blob/main/solution/0400-0499/0452.Minimum%20Number%20of%20Arrows%20to%20Burst%20Balloons/README.md)
3. **若 $a > e$**
当前区间完全不包含已有的两个点,需要补两个点。最优选择是在区间最右侧放置 $\{b - 1, b\}$。此时更新 $\textit{ans} = \textit{ans} + 2$,并将新的两点设为 $\{b - 1, b\}$。

最终返回总共放置的点数 $\textit{ans}$。

时间复杂度 $O(n \times \log n)$,空间复杂度 $O(\log n)$。其中 $n$ 为区间的数量。

<!-- tabs:start -->

Expand Down Expand Up @@ -188,6 +210,32 @@ func intersectionSizeTwo(intervals [][]int) int {
}
```

#### TypeScript

```ts
function intersectionSizeTwo(intervals: number[][]): number {
intervals.sort((a, b) => (a[1] !== b[1] ? a[1] - b[1] : b[0] - a[0]));
let s = -1;
let e = -1;
let ans = 0;
for (const [a, b] of intervals) {
if (a <= s) {
continue;
}
if (a > e) {
ans += 2;
s = b - 1;
e = b;
} else {
ans += 1;
s = e;
e = b;
}
}
return ans;
}
```

<!-- tabs:end -->

<!-- solution:end -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,33 @@ It can be shown that there cannot be any containing array of size 4.

<!-- solution:start -->

### Solution 1
### Solution 1: Sorting + Greedy

We want to select as few integer points as possible on the number line such that each interval contains at least two points. A classic and effective strategy is to sort intervals by their right endpoints and try to place selected points towards the right side of intervals, so that these points can cover more subsequent intervals.

First, sort all intervals by the following rules:

1. Sort by right endpoint in ascending order;
2. If right endpoints are equal, sort by left endpoint in descending order.

The reason for this sorting is: intervals with smaller right endpoints have less "operational space" and should be satisfied first; when right endpoints are equal, intervals with larger left endpoints are narrower and should be prioritized.

Next, we use two variables $s$ and $e$ to record the **second-to-last point** and **last point** that are common to all currently processed intervals. Initially, $s = e = -1$, indicating that no points have been placed yet.

Then we process the sorted intervals $[a, b]$ one by one, and discuss three cases based on their relationship with $\{s, e\}$:

1. **If $a \leq s$**:
The current interval already contains both points $s$ and $e$, so no additional points are needed.

2. **If $s < a \leq e$**:
The current interval only contains one point (i.e., $e$), and needs one more point. To make the new point most helpful for subsequent intervals, we choose the rightmost point $b$ in the interval. Update $\textit{ans} = \textit{ans} + 1$, and set the new two points to $\{e, b\}$.

3. **If $a > e$**:
The current interval does not contain either of the existing two points, so two points need to be added. The optimal choice is to place $\{b - 1, b\}$ at the rightmost side of the interval. Update $\textit{ans} = \textit{ans} + 2$, and set the new two points to $\{b - 1, b\}$.

Finally, return the total number of points placed, $\textit{ans}$.

The time complexity is $O(n \times \log n)$ and the space complexity is $O(\log n)$, where $n$ is the number of intervals.

<!-- tabs:start -->

Expand Down Expand Up @@ -182,6 +208,32 @@ func intersectionSizeTwo(intervals [][]int) int {
}
```

#### TypeScript

```ts
function intersectionSizeTwo(intervals: number[][]): number {
intervals.sort((a, b) => (a[1] !== b[1] ? a[1] - b[1] : b[0] - a[0]));
let s = -1;
let e = -1;
let ans = 0;
for (const [a, b] of intervals) {
if (a <= s) {
continue;
}
if (a > e) {
ans += 2;
s = b - 1;
e = b;
} else {
ans += 1;
s = e;
e = b;
}
}
return ans;
}
```

<!-- tabs:end -->

<!-- solution:end -->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
function intersectionSizeTwo(intervals: number[][]): number {
intervals.sort((a, b) => (a[1] !== b[1] ? a[1] - b[1] : b[0] - a[0]));
let s = -1;
let e = -1;
let ans = 0;
for (const [a, b] of intervals) {
if (a <= s) {
continue;
}
if (a > e) {
ans += 2;
s = b - 1;
e = b;
} else {
ans += 1;
s = e;
e = b;
}
}
return ans;
}