目录
1. T435:无重叠区间
T435:给定一个区间的集合 intervals ,其中 intervals[i] = [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 。
提示:
-
1 <= intervals.length <= 105
-
intervals[i].length == 2
-
-5 * 104 <= starti < endi <= 5 * 104
S:本题相较于上题(T452:用最少数量的箭引爆气球),需要思考的点更多,难度上又有提升~
很容易就能看出来应该要排序,但是会衍生出新的问题:
- 按左还是右边界排序?
- 排序之后,按照什么方向遍历?
其实这个问题一开始我想不明白,但看了Carl的讲解后理解,可分为以下两种情况(均为升序):
-
按照右边界排序,就要从左向右遍历,因为右边界越小越好,只要右边界越小,留给下一个区间的空间就越大,所以从左向右遍历,优先选右边界小的。
-
按照左边界排序,就要从右向左遍历,因为左边界数值越大越好(越靠右),这样就给前一个区间的空间就越大,所以可以从右向左遍历。
以右边界排序为例,排序之后,贪心分析:
-
局部最优:优先选右边界小的区间,所以从左向右遍历,留给下一个区间的空间大一些,从而尽量避免交叉。
-
全局最优:选取最多的非交叉区间。
1.1 版本Ⅰ、按右边界排序
C++:
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
if (intervals.size() == 1) return 0;
sort(intervals.begin(), intervals.end(), cmp);
// int count = 0;
int count = 1;
int curEnd = intervals[0][1];
// for (vector<int> interval : intervals) {
for (int i = 1; i < intervals.size(); ++i) {
// if (interval[0] > curEnd) {// 等于不算重叠
if (intervals[i][0] >= curEnd) {
++count;
curEnd = intervals[i][1];
}
}
return intervals.size() - count;
}
static bool cmp(const vector<int>& i1, const vector<int>& i2) {
return i1[1] < i2[1];
}
Java:
public int eraseOverlapIntervals(int[][] intervals) {
if (intervals.length == 1) return 0;
Arrays.sort(intervals, (i1, i2) -> i1[1] - i2[1]);
// int count = 0;
int count = 1;
int curEnd = intervals[0][1];
for (int i = 1; i < intervals.length; ++i) {
if (intervals[i][0] >= curEnd) {
++count;
curEnd = intervals[i][1];
}
}
return intervals.length - count;
}
1.2 版本Ⅱ、按左边界排序
C++:
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
if (intervals.size() == 1) return 0;
sort(intervals.begin(), intervals.end(), cmp);
int count = 1;
int curSplit = intervals[intervals.size() - 1][0];
for (int i = intervals.size() - 2; i >= 0; --i) {
if (intervals[i][1] <= curSplit) {
++count;
curSplit = intervals[i][0];
}
}
return intervals.size() - count;
}
static bool cmp(const vector<int>& i1, const vector<int>& i2) {
return i1[0] < i2[0];
}
Java:
public int eraseOverlapIntervals(int[][] intervals) {
if (intervals.length == 1) return 0;
Arrays.sort(intervals, (i1, i2) -> i1[0] - i2[0]);
// int count = 0;
int count = 1;
int curSplit = intervals[intervals.length - 1][0];
for (int i = intervals.length - 2; i >= 0; --i) {
if (intervals[i][1] <= curSplit) {
++count;
curSplit = intervals[i][0];
}
}
return intervals.length - count;
}
2. T763:划分字母区间
T763:给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。
注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s 。
返回一个表示每个字符串片段的长度的列表。
提示:
-
1 <= s.length <= 500
-
s 仅由小写英文字母组成
S:一上来挺懵的,还在想莫不是又要像之前的分割回文串或者还原IP地址那样(其实这两题也都差不多忘了)
通过之后其实还是有点懵,仍有一未解之谜:如何实现确保“划分为尽可能多的片段”?
C++:
vector<int> partitionLabels(string s) {
// i为字符,hash[i]为字符出现的最后位置
int hash[26] = {0};// 全部初始化为0
// int hash[25] = {0};// 索引越界
for (int i = 0; i < s.size(); ++i) {
hash[s[i] - 'a'] = i;
}
vector<int> res;
int left = 0, right = 0;
for (int i = 0; i < s.size(); ++i) {
right = max(right, hash[s[i] - 'a']);// 🚩
if (i == right) {
res.push_back(right - left + 1);
left = i + 1;
}
}
return res;
}
Java:
public List<Integer> partitionLabels(String s) {
char[] str = s.toCharArray();
int[] hash = new int[26];
for (int i = 0; i < str.length; ++i) {
hash[str[i] - 'a'] = i;
}
List<Integer> res = new ArrayList<>();
int left = 0, right = 0;
for (int i = 0; i < str.length; ++i) {
right = Math.max(right, hash[str[i] - 'a']);
if (i == right) {
res.add(right - left + 1);
left = i + 1;
}
}
return res;
}
3. T56:合并区间
T56:以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
提示:
-
1 <= intervals.length <= 104
-
intervals[i].length == 2
-
0 <= starti <= endi <= 104
S:带着前几题的经验,差一点做出来,就是for循环里的逻辑没有完全想出来。。。
C++:第一次在C++代码中用Lambda表达式~
vector<vector<int>> merge(vector<vector<int>>& intervals) {
if (intervals.size() == 1) return intervals;
// sort(intervals.begin(), intervals.end(), cmp);
sort(intervals.begin(), intervals.end(), [](const vector<int>& i1, const vector<int>& i2) {return i1[0] < i2[0];});
vector<vector<int>> res;
res.push_back(intervals[0]);
for (int i = 1; i < intervals.size(); ++i) {
// if (intervals[i][0] > intervals[i - 1][1]) {
if (intervals[i][0] > res.back()[1]) {
res.push_back(intervals[i]);
} else {// 🚩核心步骤
res.back()[1] = max(res.back()[1], intervals[i][1]);
}
}
return res;
}
// static bool cmp(const vector<int>& i1, const vector<int>& i2) {
// if (i1[0] == i2[0]) return i1[1] < i2[1];
// return i1[0] < i2[0];
// }
Java:
public int[][] merge(int[][] intervals) {
if (intervals.length == 1) return intervals;
Arrays.sort(intervals, (i1, i2) -> i1[0] - i2[0]);// 本题的范围没有溢出风险
// int[][] res = new int[intervals.length][];// 这样得不到back
LinkedList<int[]> res = new LinkedList<>();
res.offerLast(intervals[0]);
for (int i = 1; i < intervals.length; ++i) {
if (intervals[i][0] <= res.getLast()[1]) {
int start = res.getLast()[0];
int end = res.pollLast()[1];
res.add(new int[]{start, Math.max(intervals[i][1], end)});
} else {
res.addLast(intervals[i]);
}
}
return res.toArray(new int[res.size()][]);
}