当前位置:网站首页>Leetcode(435)——无重叠区间
Leetcode(435)——无重叠区间
2022-06-25 21:59:00 【SmileGuy17】
Leetcode(435)——无重叠区间
题目
给定一个区间的集合 intervals ,其中 intervals[i] = [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 。
示例 1:
输入: intervals = [[1,2],[2,3],[3,4],[1,3]]
输出: 1
解释: 移除 [1,3] 后,剩下的区间没有重叠。
示例 2:
输入: intervals = [ [1,2], [1,2], [1,2] ]
输出: 2
解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。
示例 3:
输入: intervals = [ [1,2], [2,3] ]
输出: 0
解释: 你不需要移除任何区间,因为它们已经是无重叠的了。
提示:
- 1 1 1 <= intervals.length <= 1 0 5 10^5 105
- intervals[i].length == 2 2 2
- − 5 ∗ 1 0 4 -5 * 10^4 −5∗104 <= starti < endi <= 5 ∗ 1 0 4 5 * 10^4 5∗104
题解
方法一:贪心算法
思路
我们不妨想一想应该选择哪一个区间作为首个区间。
假设在某一种 最优 的选择方法中, [ l k , r k ] [l_k, r_k] [lk,rk] 是首个(即最左侧的)区间,那么它的左侧没有其它区间,右侧有若干个不重叠的区间。设想一下,如果此时存在一个区间 [ l j , r j ] [l_j, r_j] [lj,rj],使得 r j < r k r_j < r_k rj<rk,即区间 j j j 的右端点在区间 k k k 的左侧,那么我们将区间 k k k 替换为区间 j j j,其与剩余右侧被选择的区间仍然是不重叠的。而当我们将区间 k k k 替换为区间 j j j 后,就得到了另一种 最优 的选择方法。
我们可以不断地寻找右端点在首个区间右端点左侧的新区间,将首个区间替换成该区间。那么当我们无法替换时,首个区间就是所有可以选择的区间中右端点最小的那个区间。因此我们将所有区间按照右端点从小到大进行排序,那么排完序之后的首个区间,就是我们选择的首个区间。
在选择首个区间时,如果有多个区间的右端点都同样最小怎么办?由于我们选择的是首个区间,因此在它的左侧不会有其它的区间,那么左端点在何处是不重要的,我们只要任意选择一个右端点最小的区间即可。
当确定了首个区间之后,所有与首个区间不重合的区间就组成了一个规模更小的子问题。由于我们已经在初始时将所有区间按照右端点排好序了,因此对于这个子问题,我们无需再次进行排序,只要找出其中与首个区间不重合并且右端点最小的区间即可。用相同的方法,我们可以依次确定后续的所有区间。
在实际的代码编写中,我们对按照右端点排好序的区间进行遍历,并且实时维护上一个选择区间的右端点 right \textit{right} right。如果当前遍历到的区间 [ l i , r i ] [l_i, r_i] [li,ri] 与上一个区间不重合,即 l i ≥ right l_i \geq \textit{right} li≥right,那么我们就可以贪心地选择这个区间,并将 right \textit{right} right 更新为 r i r_i ri。
代码实现
class Solution {
public:
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
if(intervals.empty()) return 0;
int n = intervals.size();
sort(intervals.begin(), intervals.end(),
[](vector<int>& a, vector<int>& b){
return a[1] < b[1];});
int total = 0, prev = intervals[0][1];
for(int i = 1; i < n; ++i) {
if(intervals[i][0] < prev) ++total;
else prev = intervals[i][1];
}
return total;
}
};
复杂度分析
时间复杂度: O ( n log n ) O(n \log n) O(nlogn),其中 n n n 是区间的数量。我们需要 O ( n log n ) O(n \log n) O(nlogn) 的时间对所有的区间按照右端点进行升序排序,并且需要 O ( n ) O(n) O(n) 的时间进行遍历。由于前者在渐进意义下大于后者,因此总时间复杂度为 O ( n log n ) O(n \log n) O(nlogn)。
空间复杂度: O ( l o g n ) O(logn) O(logn),即为排序需要使用的栈空间。
方法二:动态规划
思路
因为题目的要求等价于「选出最多数量的区间,使得它们互不重叠」。由于选出的区间互不重叠,因此我们可以将它们按照端点从小到大的顺序进行排序,并且无论我们按照左端点还是右端点进行排序,得到的结果都是唯一的。
这样一来,我们可以先将所有的 n n n 个区间按照左端点(或者右端点)从小到大进行排序,随后使用动态规划的方法求出区间数量的最大值。设排完序后这 n n n 个区间的左右端点分别为 l 0 , ⋯ , l n − 1 l_0, \cdots, l_{n-1} l0,⋯,ln−1 以及 r 0 , ⋯ , r n − 1 r_0, \cdots, r_{n-1} r0,⋯,rn−1,那么我们令 f i f_i fi 表示「从区间 0 0 0 到区间 i i i 的这些区间中,在最后一个区间为 i i i 的情况下,可以选出最多的无重叠的区间的数量」,状态转移方程即为:
f i = max j < i ∧ r j ≤ l i { f j } + 1 f_i = \max_{j < i ~\wedge~ r_j \leq l_i} \{ f_j \} + 1 fi=j<i ∧ rj≤limax{ fj}+1
即我们枚举倒数第二个区间的编号 j j j,满足 j < i j < i j<i,并且第 j j j 个区间必须要与第 i i i 个区间不重叠。由于我们已经按照左端点进行升序排序了,因此只要第 j j j 个区间的右端点 r j r_j rj 没有越过第 i i i 个区间的左端点 l i l_i li,即 r j ≤ l i r_j \leq l_i rj≤li,那么第 j j j 个区间就与第 i i i 个区间不重叠。我们在所有满足要求的 j j j 中,选择 f j f_j fj 最大的那一个进行状态转移,如果找不到满足要求的区间,那么状态转移方程中 max \max max 这一项就为 0 0 0, f i f_i fi 就为 1 1 1。
最终的答案即为所有 f i f_i fi 中的最大值。
代码实现
class Solution {
public:
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
if (intervals.empty()) {
return 0;
}
sort(intervals.begin(), intervals.end(), [](const auto& u, const auto& v) {
return u[0] < v[0];
});
int n = intervals.size();
vector<int> f(n, 1);
for (int i = 1; i < n; ++i) {
for (int j = 0; j < i; ++j) {
if (intervals[j][1] <= intervals[i][0]) {
f[i] = max(f[i], f[j] + 1);
}
}
}
return n - *max_element(f.begin(), f.end());
}
};
复杂度分析
时间复杂度: O ( n 2 ) O(n^2) O(n2),其中 n n n 是区间的数量。我们需要 O ( n log n ) O(n \log n) O(nlogn) 的时间对所有的区间按照左端点进行升序排序,并且需要 O ( n 2 ) O(n^2) O(n2) 的时间进行动态规划。由于前者在渐进意义下小于后者,因此总时间复杂度为 O ( n 2 ) O(n^2) O(n2)。
注意到方法二本质上是一个「最长上升子序列」问题,因此我们可以将时间复杂度优化至 O ( n log n ) O(n \log n) O(nlogn),具体可以参考「300. 最长递增子序列的官方题解」。
空间复杂度: O ( n ) O(n) O(n),即为存储所有状态 f i f_i fi 需要的空间。
边栏推荐
- What is CDN acceleration
- 提问的智慧?如何提问?
- [opencv450 samples] inpaint restores the selected region in the image using the region neighborhood
- What aspects should we start with in the feasibility analysis of dry goods?
- How to solve the problem of SQL?
- NLP text summary: use the pre training model to perform text summary tasks [transformers:pipeline, T5, Bart, Pegasus]
- 2. What is the geometric meaning of a vector multiplying its transpose?
- Several optimization scenarios using like fuzzy retrieval in SQL
- [eosio] eos/wax signature error is_ Canonical (c): signature is not canonical
- 为什么OpenCV计算的帧率是错误的?
猜你喜欢

No absurd tea applet - rule change

Unity的Ping類使用

Several optimization scenarios using like fuzzy retrieval in SQL

The Ping class of unity uses

【EOSIO】EOS/WAX签名错误 is_canonical( c ): signature is not canonical 问题

The applet draws a simple pie chart

ES6 -- formal parameter setting initial value, extension operator, iterator, and generating function

Fastjson反序列化随机性失败

ES6学习-- LET

ES6 --- 数值扩展、对象拓展
随机推荐
zabbix_ Server configuration file details
zabbix_server配置文件详解
ES6 -- 形参设置初始值、拓展运算符、迭代器、生成函数
Technology blog site collection
MySQL数据库索引
论文笔记: 多标签学习 MSWL
Unity technical manual - life cycle rotation rotationoverlifetime- speed rotation rotationbyspeed- and external forces
Network security project questions of the first Henan vocational skills competition in 2022
Comp2913 database
判断预约时间是否已经过期
Huawei cloud SRE deterministic operation and maintenance special issue (the first issue)
问题记录与思考
ES6 const constants and array deconstruction
【opencv450 samples】创建图像列表yaml
如何用jmeter做接口测试
[modulebuilder] GP service realizes the intersection selection of two layers in SDE
关闭MongoDB一些服务需要注意的地方(以及开启的相关命令)
Problem recording and thinking
多模态数据也能进行MAE?伯克利&谷歌提出M3AE,在图像和文本数据上进行MAE!最优掩蔽率可达75%,显著高于BERT的15%...
Implementation of importing vscode from PDM