给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。
注意: 区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。
输入: intervals = [[1,2],[2,3],[3,4],[1,3]] 输出: 1 解释: 移除 [1,3] 后,剩下的区间没有重叠。文章讲解https://programmercarl.com/0435.%E6%97%A0%E9%87%8D%E5%8F%A0%E5%8C%BA%E9%97%B4.html
class Solution {
private:
static bool cmp(const vector& a, const vector& b) {
return a[0] < b[0];
}
public:
int eraseOverlapIntervals(vector>& intervals) {
// 按start从小到大排列
sort(intervals.begin(), intervals.end(), cmp);
// 统计重叠区间数
int result = 0;
for (int i = 1; i < intervals.size(); ++i) {
if (intervals[i][0] < intervals[i - 1][1]) { // 重叠
++result; // 移除一个重叠区间
intervals[i][1] = min(intervals[i - 1][1], intervals[i][1]); // 保留右边界小的那个重叠区间
}
}
return result;
}
};
LeetCdoe 763.划分字母区间
字符串 s 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。
输入:s = "ababcbacadefegdehijhklij"
输出:[9,7,8]
解释:
划分结果为 "ababcbaca"、"defegde"、"hijhklij" 。
每个字母最多出现在一个片段中。
像 "ababcbacadefegde", "hijhklij" 这样的划分是错误的,因为划分的片段数较少。
// 代码随想录:
class Solution {
public:
vector partitionLabels(string s) {
int hash[26] = {0}; // i为字符,hash[i]为字符出现的最后位置
for (int i = 0; i < s.size(); ++i) { // 统计每一个字符最后出现的位置
hash[s[i] - 'a'] = i;
}
vector result;
int left = 0;
int right = 0;
for (int i = 0; i < s.size(); ++i) {
right = max(right, hash[s[i] - 'a']); // 找到字符出现的最远边界
if (i == right) {
result.push_back(right - left + 1);
left = i + 1;
}
}
return result;
}
};
// 时间复杂度:$O(n)$
// 空间复杂度:$O(1)$,使用的hash数组是固定大小
// 自己:
class Solution {
private:
static bool cmp(const vector& a, const vector& b) {
return a[0] < b[0];
}
public:
vector partitionLabels(string s) {
vector result;
if (s.size() == 0) return result;
// 统计26个小写字母出现下标[start, end]
// -1 表示未出现
vector> letters(26, vector(2, -1));
for (int i = 0; i < s.size(); ++i) {
int index = s[i] - 'a';
if (letters[index][0] != -1) { // 当前字母出现过,更新右边界
letters[index][1] = i;
} else { // 当前字母未出现,更新左右边界
letters[index][0] = i;
letters[index][1] = i;
}
}
// 按start从小到大排序
sort(letters.begin(), letters.end(), cmp);
int i = 0;
while (letters[i][0] == -1 && i < 26) {
++i;
}
if (i == 26) return result;
// i定位到有效部分
int left = letters[i][0];
int right = letters[i][1];
for (int j = i + 1; j < 26; ++j) {
if (right < letters[j][0]) { // 不重叠
// 收获现有[left, right]区间长度
result.push_back(right + 1 - left);
// 更新重叠区间
left = letters[j][0];
right = letters[j][1];
} else { // 合并重叠区间
left = min(letters[j][0], left);
right = max(letters[j][1], right);
}
}
// 收获最后一个[left, right]区间长度
result.push_back(right + 1 - left);
return result;
}
};
LeetCdoe 56. 合并区间
给出一个区间的集合,请合并所有重叠的区间。
// 代码随想录:
class Solution {
public:
vector> merge(vector>& intervals) {
vector> result;
if (intervals.size() == 0) return result;
// 排序的参数使用了lambda表达式
sort(intervals.begin(), intervals.end(), [](const vector& a, const vector& b){return a[0] < b[0];});
result.push_back(intervals[0]);
for (int i = 1; i < intervals.size(); i++) {
if (result.back()[1] >= intervals[i][0]) { // 合并区间
result.back()[1] = max(result.back()[1], intervals[i][1]);
} else { // 不重叠,直接收获
result.push_back(intervals[i]);
}
}
return result;
}
}
// 时间复杂度:O(nlog n) ,有一个快排
// 空间复杂度:O(n),有一个快排,最差情况(倒序)时,需要n次递归调用。因此确实需要O(n)的栈空间
// 自己:
class Solution {
private:
static bool cmp(const vector& a, const vector& b) {
return a[0] < b[0];
}
public:
vector> merge(vector>& intervals) {
vector> result;
// 按start从小到大排序
sort(intervals.begin(), intervals.end(), cmp);
int left = intervals[0][0];
int right = intervals[0][1];
for (int i = 1; i < intervals.size(); ++i) {
if (right < intervals[i][0]) { // 不重叠
// [left, right]不用再合并,加入结果集
result.push_back({left, right});
// 更新现有区间
left = intervals[i][0];
right = intervals[i][1];
} else { // 重叠
// 合并重叠区间
right = max(intervals[i][1], right);
}
}
// 收获最后一个[left, right]
result.push_back({left, right});
return result;
}
};